Lely core libraries  2.2.5
file.c
Go to the documentation of this file.
1 
24 #ifdef __linux__
25 // This needs to be defined before any files are included to make lseek64()
26 // available.
27 #define _LARGEFILE64_SOURCE 1
28 #endif
29 
30 #include "default.h"
31 #include "io.h"
32 #include <lely/io/file.h>
33 
34 #include <assert.h>
35 
36 #if defined(_WIN32) || _POSIX_C_SOURCE >= 200112L
37 
39 struct file {
41  struct io_handle base;
47  int flags;
48 };
49 
50 static ssize_t file_read(struct io_handle *handle, void *buf, size_t nbytes);
51 static ssize_t file_write(
52  struct io_handle *handle, const void *buf, size_t nbytes);
53 static int file_flush(struct io_handle *handle);
54 io_off_t file_seek(struct io_handle *handle, io_off_t offset, int whence);
55 static ssize_t file_pread(struct io_handle *handle, void *buf, size_t nbytes,
56  io_off_t offset);
57 static ssize_t file_pwrite(struct io_handle *handle, const void *buf,
58  size_t nbytes, io_off_t offset);
59 
60 static const struct io_handle_vtab file_vtab = { .type = IO_TYPE_FILE,
61  .size = sizeof(struct file),
62  .fini = &default_fini,
63  .read = &file_read,
64  .write = &file_write,
65  .flush = &file_flush,
66  .pread = &file_pread,
67  .pwrite = &file_pwrite };
68 
69 #ifdef _WIN32
70 static ssize_t _file_read(struct io_handle *handle, void *buf, size_t nbytes,
71  io_off_t offset);
72 static ssize_t _file_write(struct io_handle *handle, const void *buf,
73  size_t nbytes, io_off_t offset);
74 #endif
75 
77 io_open_file(const char *path, int flags)
78 {
79  assert(path);
80 
81  int errc = 0;
82 
83  if (!(flags & (IO_FILE_READ | IO_FILE_WRITE))) {
84  errc = errnum2c(ERRNUM_INVAL);
85  goto error_param;
86  }
87 
88  if (!(flags & IO_FILE_WRITE))
91  if (!(flags & IO_FILE_CREATE))
93  if (flags & IO_FILE_NO_EXIST)
95 
96 #ifdef _WIN32
97  DWORD dwDesiredAccess = 0;
98  if (flags & IO_FILE_READ)
99  dwDesiredAccess |= FILE_READ_DATA;
100  if (flags & IO_FILE_APPEND) {
101  dwDesiredAccess |= FILE_APPEND_DATA;
102  } else if (flags & IO_FILE_WRITE) {
103  dwDesiredAccess |= FILE_WRITE_DATA;
104  }
105 
106  // clang-format off
107  DWORD dwCreationDisposition
109  ? CREATE_NEW
111  ? CREATE_ALWAYS
112  : (flags & IO_FILE_CREATE) ? OPEN_ALWAYS
113  : (flags & IO_FILE_TRUNCATE) ? TRUNCATE_EXISTING
114  : OPEN_EXISTING;
115  // clang-format on
116 
117  HANDLE fd = CreateFileA(path, dwDesiredAccess, 0, NULL,
118  dwCreationDisposition, FILE_FLAG_OVERLAPPED, NULL);
119 #else
120  // clang-format off
121  int oflag = (flags & IO_FILE_READ) && (flags & IO_FILE_WRITE) ? O_RDWR
122  : (flags & IO_FILE_READ) ? O_RDONLY
123  : (flags & IO_FILE_WRITE) ? O_WRONLY
124  : 0;
125  // clang-format on
126  if (flags & IO_FILE_APPEND)
127  oflag |= O_APPEND;
128  if (flags & IO_FILE_CREATE) {
129  oflag |= O_CREAT;
130  if (flags & IO_FILE_NO_EXIST)
131  oflag |= O_EXCL;
132  }
133  if (flags & IO_FILE_TRUNCATE)
134  oflag |= O_TRUNC;
135  oflag |= O_CLOEXEC;
136 
137  // When creating a new file, enable read permission for all, and write
138  // permission for the owner and the group (664). Note that this value is
139  // modified by the process' umask.
140  mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
141 
142  int fd;
143  int errsv = errno;
144  do {
145  errno = errsv;
146  fd = open(path, oflag, mode);
147  } while (fd == -1 && errno == EINTR);
148 #endif
149 
150  if (fd == INVALID_HANDLE_VALUE) {
151  errc = get_errc();
152  goto error_open;
153  }
154 
155  struct io_handle *handle = io_handle_alloc(&file_vtab);
156  if (!handle) {
157  errc = get_errc();
158  goto error_alloc_handle;
159  }
160 
161  handle->fd = fd;
162  ((struct file *)handle)->flags = flags;
163 
164  return io_handle_acquire(handle);
165 
166 error_alloc_handle:
167 #ifdef _WIN32
168  CloseHandle(fd);
169 #else
170  close(fd);
171 #endif
172 error_open:
173 error_param:
174  set_errc(errc);
175  return IO_HANDLE_ERROR;
176 }
177 
178 #endif // _WIN32 || _POSIX_C_SOURCE >= 200112L
179 
180 io_off_t
181 io_seek(io_handle_t handle, io_off_t offset, int whence)
182 {
183  if (handle == IO_HANDLE_ERROR) {
185  return -1;
186  }
187 
188  assert(handle->vtab);
189  if (!handle->vtab->seek) {
191  return -1;
192  }
193 
194  return handle->vtab->seek(handle, offset, whence);
195 }
196 
197 ssize_t
198 io_pread(io_handle_t handle, void *buf, size_t nbytes, io_off_t offset)
199 {
200  if (handle == IO_HANDLE_ERROR) {
202  return -1;
203  }
204 
205  assert(handle->vtab);
206  if (!handle->vtab->pread) {
208  return -1;
209  }
210 
211  return handle->vtab->pread(handle, buf, nbytes, offset);
212 }
213 
214 ssize_t
215 io_pwrite(io_handle_t handle, const void *buf, size_t nbytes, io_off_t offset)
216 {
217  if (handle == IO_HANDLE_ERROR) {
219  return -1;
220  }
221 
222  assert(handle->vtab);
223  if (!handle->vtab->pwrite) {
225  return -1;
226  }
227 
228  return handle->vtab->pwrite(handle, buf, nbytes, offset);
229 }
230 
231 #if defined(_WIN32) || _POSIX_C_SOURCE >= 200112L
232 
233 static ssize_t
234 file_read(struct io_handle *handle, void *buf, size_t nbytes)
235 {
236  assert(handle);
237 
238 #ifdef _WIN32
239  io_off_t current = file_seek(handle, 0, IO_SEEK_CURRENT);
240  if (current == -1)
241  return -1;
242  return _file_read(handle, buf, nbytes, current);
243 #else
244  ssize_t result;
245  int errsv = errno;
246  do {
247  errno = errsv;
248  result = read(handle->fd, buf, nbytes);
249  } while (result == -1 && errno == EINTR);
250  return result;
251 #endif
252 }
253 
254 static ssize_t
255 file_write(struct io_handle *handle, const void *buf, size_t nbytes)
256 {
257  assert(handle);
258 
259 #ifdef _WIN32
260  io_off_t current;
261  if (((struct file *)handle)->flags & IO_FILE_APPEND) {
262  // This value of the offset causes WriteFile() to write to the
263  // end of file.
264  current = UINT64_MAX;
265  } else {
266  current = file_seek(handle, 0, IO_SEEK_CURRENT);
267  if (current == -1)
268  return -1;
269  }
270  return _file_write(handle, buf, nbytes, current);
271 #else
272  ssize_t result;
273  int errsv = errno;
274  do {
275  errno = errsv;
276  result = write(handle->fd, buf, nbytes);
277  } while (result == -1 && errno == EINTR);
278  return result;
279 #endif
280 }
281 
282 static int
283 file_flush(struct io_handle *handle)
284 {
285  assert(handle);
286 
287 #ifdef _WIN32
288  return FlushFileBuffers(handle->fd) ? 0 : -1;
289 #else
290  int result;
291  int errsv = errno;
292  do {
293  errno = errsv;
294  result = fsync(handle->fd);
295  } while (result == -1 && errno == EINTR);
296  return result;
297 #endif
298 }
299 
300 io_off_t
301 file_seek(struct io_handle *handle, io_off_t offset, int whence)
302 {
303  assert(handle);
304 
305 #ifdef _WIN32
306  DWORD dwMoveMethod;
307  switch (whence) {
308  case IO_SEEK_BEGIN: dwMoveMethod = FILE_BEGIN; break;
309  case IO_SEEK_CURRENT: dwMoveMethod = FILE_CURRENT; break;
310  case IO_SEEK_END: dwMoveMethod = FILE_END; break;
311  default: SetLastError(ERROR_INVALID_PARAMETER); return -1;
312  }
313 
314  LARGE_INTEGER li;
315  li.QuadPart = offset;
316  li.LowPart = SetFilePointer(
317  handle->fd, li.LowPart, &li.HighPart, dwMoveMethod);
318  if (li.LowPart == INVALID_SET_FILE_POINTER)
319  return -1;
320  return li.QuadPart;
321 #else
322  switch (whence) {
323  case IO_SEEK_BEGIN: whence = SEEK_SET; break;
324  case IO_SEEK_CURRENT: whence = SEEK_CUR; break;
325  case IO_SEEK_END: whence = SEEK_END; break;
326  default: errno = EINVAL; return -1;
327  }
328 
329 #if defined(__linux__)
330  return lseek64(handle->fd, offset, whence);
331 #else
332  return lseek(handle->fd, offset, whence);
333 #endif
334 #endif
335 }
336 
337 static ssize_t
338 file_pread(struct io_handle *handle, void *buf, size_t nbytes, io_off_t offset)
339 {
340  assert(handle);
341 
342 #ifdef _WIN32
343  io_off_t current = file_seek(handle, 0, IO_SEEK_CURRENT);
344  if (current == -1)
345  return -1;
346  ssize_t result = _file_read(handle, buf, nbytes, offset);
347  if (result == -1)
348  return -1;
349  // pread() does not change the file pointer.
350  if (file_seek(handle, current, IO_SEEK_BEGIN) == -1)
351  return -1;
352  return result;
353 #else
354  ssize_t result;
355  int errsv = errno;
356  do {
357  errno = errsv;
358  result = pread(handle->fd, buf, nbytes, offset);
359  } while (result == -1 && errno == EINTR);
360  return result;
361 #endif
362 }
363 
364 static ssize_t
365 file_pwrite(struct io_handle *handle, const void *buf, size_t nbytes,
366  io_off_t offset)
367 {
368  assert(handle);
369 
370 #ifdef _WIN32
371  io_off_t current = file_seek(handle, 0, IO_SEEK_CURRENT);
372  if (current == -1)
373  return -1;
374  ssize_t result = _file_write(handle, buf, nbytes, offset);
375  if (result == -1)
376  return -1;
377  // pwrite() does not change the file pointer.
378  if (file_seek(handle, current, IO_SEEK_BEGIN) == -1)
379  return -1;
380  return result;
381 #else
382  ssize_t result;
383  int errsv = errno;
384  do {
385  errno = errsv;
386  result = pwrite(handle->fd, buf, nbytes, offset);
387  } while (result == -1 && errno == EINTR);
388  return result;
389 #endif
390 }
391 
392 #ifdef _WIN32
393 
394 static ssize_t
395 _file_read(struct io_handle *handle, void *buf, size_t nbytes, io_off_t offset)
396 {
397  assert(handle);
398 
399  DWORD dwErrCode = GetLastError();
400 
401  LARGE_INTEGER li;
402  li.QuadPart = offset;
403  OVERLAPPED overlapped = { 0 };
404  overlapped.Offset = li.LowPart;
405  overlapped.OffsetHigh = li.HighPart;
406  overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
407  if (!overlapped.hEvent) {
408  dwErrCode = GetLastError();
409  goto error_CreateEvent;
410  }
411 
412  DWORD dwNumberOfBytesRead = 0;
413 
414  // clang-format off
415  if (ReadFile(handle->fd, buf, nbytes, &dwNumberOfBytesRead,
416  &overlapped))
417  // clang-format on
418  goto done;
419 
420  if (GetLastError() != ERROR_IO_PENDING) {
421  dwErrCode = GetLastError();
422  goto error_ReadFile;
423  }
424 
425  // clang-format off
426  if (!GetOverlappedResult(handle->fd, &overlapped, &dwNumberOfBytesRead,
427  TRUE)) {
428  // clang-format on
429  dwErrCode = GetLastError();
430  goto error_GetOverlappedResult;
431  }
432 
433 done:
434  CloseHandle(overlapped.hEvent);
435  SetLastError(dwErrCode);
436  return dwNumberOfBytesRead;
437 
438 error_GetOverlappedResult:
439 error_ReadFile:
440  CloseHandle(overlapped.hEvent);
441 error_CreateEvent:
442  SetLastError(dwErrCode);
443  return -1;
444 }
445 
446 static ssize_t
447 _file_write(struct io_handle *handle, const void *buf, size_t nbytes,
448  io_off_t offset)
449 {
450  assert(handle);
451 
452  DWORD dwErrCode = GetLastError();
453 
454  LARGE_INTEGER li;
455  li.QuadPart = offset;
456  OVERLAPPED overlapped = { 0 };
457  overlapped.Offset = li.LowPart;
458  overlapped.OffsetHigh = li.HighPart;
459  overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
460  if (!overlapped.hEvent) {
461  dwErrCode = GetLastError();
462  goto error_CreateEvent;
463  }
464 
465  DWORD dwNumberOfBytesWritten = 0;
466 
467  // clang-format off
468  if (WriteFile(handle->fd, buf, nbytes, &dwNumberOfBytesWritten,
469  &overlapped))
470  // clang-format on
471  goto done;
472 
473  if (GetLastError() != ERROR_IO_PENDING) {
474  dwErrCode = GetLastError();
475  goto error_WriteFile;
476  }
477 
478  // clang-format off
479  if (!GetOverlappedResult(handle->fd, &overlapped,
480  &dwNumberOfBytesWritten, TRUE)) {
481  // clang-format on
482  dwErrCode = GetLastError();
483  goto error_GetOverlappedResult;
484  }
485 
486 done:
487  CloseHandle(overlapped.hEvent);
488  SetLastError(dwErrCode);
489  return dwNumberOfBytesWritten;
490 
491 error_GetOverlappedResult:
492 error_WriteFile:
493  CloseHandle(overlapped.hEvent);
494 error_CreateEvent:
495  SetLastError(dwErrCode);
496  return -1;
497 }
498 
499 #endif // _WIN32
500 
501 #endif // _WIN32 || _POSIX_C_SOURCE >= 200112L
IO_FILE_READ
@ IO_FILE_READ
Open a file for reading.
Definition: file.h:29
IO_FILE_APPEND
@ IO_FILE_APPEND
Append data to the end of the file.
Definition: file.h:33
io_handle_vtab::seek
io_off_t(* seek)(struct io_handle *handle, io_off_t offset, int whence)
A pointer to the seek method.
Definition: handle.h:94
io_pwrite
ssize_t io_pwrite(io_handle_t handle, const void *buf, size_t nbytes, io_off_t offset)
Performs a write operation at the specified offset, without updating the file pointer.
Definition: file.c:215
io_open_file
io_handle_t io_open_file(const char *path, int flags)
Opens a regular file.
Definition: file.c:77
IO_FILE_CREATE
@ IO_FILE_CREATE
Create a new file if it does not exists.
Definition: file.h:35
IO_FILE_NO_EXIST
@ IO_FILE_NO_EXIST
Fail if the file already exists (ignored unless IO_FILE_CREATE is set).
Definition: file.h:40
IO_FILE_WRITE
@ IO_FILE_WRITE
Open a file for writing.
Definition: file.h:31
file
A regular file handle.
Definition: file.c:39
io_off_t
int64_t io_off_t
A file offset type.
Definition: io.h:37
IO_FILE_TRUNCATE
@ IO_FILE_TRUNCATE
Truncate an existing file (ignored if IO_FILE_NO_EXIST is set).
Definition: file.h:42
io_pread
ssize_t io_pread(io_handle_t handle, void *buf, size_t nbytes, io_off_t offset)
Performs a read operation at the specified offset, without updating the file pointer.
Definition: file.c:198
io_handle_vtab::type
int type
The type of the device (one of IO_TYPE_CAN, IO_TYPE_FILE, IO_TYPE_PIPE, IO_TYPE_SERIAL or IO_TYPE_SOC...
Definition: handle.h:79
get_errc
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function.
Definition: errnum.c:947
io_handle
An I/O device handle.
Definition: handle.h:41
io_handle_acquire
io_handle_t io_handle_acquire(io_handle_t handle)
Increments the reference count of an I/O device handle.
Definition: handle.c:32
io_seek
io_off_t io_seek(io_handle_t handle, io_off_t offset, int whence)
Moves the current read/write offset of an open file.
Definition: file.c:181
ERRNUM_BADF
@ ERRNUM_BADF
Bad file descriptor.
Definition: errnum.h:90
io_handle_alloc
struct io_handle * io_handle_alloc(const struct io_handle_vtab *vtab)
Allocates a new I/O device handle from a virtual table.
Definition: handle.c:77
set_errnum
void set_errnum(errnum_t errnum)
Sets the current (thread-specific) platform-independent error number to errnum.
Definition: errnum.h:375
IO_SEEK_END
@ IO_SEEK_END
A seek operation with respect to the end of a file.
Definition: file.h:51
set_errc
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:957
io.h
errnum2c
int errnum2c(errnum_t errnum)
Transforms a platform-independent error number to a native error code.
Definition: errnum.c:825
IO_HANDLE_ERROR
#define IO_HANDLE_ERROR
The value of an invalid I/O device handle.
Definition: io.h:34
io_handle::vtab
const struct io_handle_vtab * vtab
A pointer to the virtual table.
Definition: handle.h:43
IO_SEEK_CURRENT
@ IO_SEEK_CURRENT
A seek operation with respect to the current offset in a file.
Definition: file.h:49
ERRNUM_INVAL
@ ERRNUM_INVAL
Invalid argument.
Definition: errnum.h:129
IO_SEEK_BEGIN
@ IO_SEEK_BEGIN
A seek operation with respect to the beginning of a file.
Definition: file.h:47
file::base
struct io_handle base
The I/O device base handle.
Definition: file.c:41
file::flags
int flags
The file flags (any combination of IO_FILE_READ, IO_FILE_WRITE, IO_FILE_APPEND, IO_FILE_CREATE,...
Definition: file.c:47
io_handle_vtab::pread
ssize_t(* pread)(struct io_handle *handle, void *buf, size_t nbytes, io_off_t offset)
A pointer to the pread method.
Definition: handle.h:96
ERRNUM_SPIPE
@ ERRNUM_SPIPE
Invalid seek.
Definition: errnum.h:219
io_handle_vtab::pwrite
ssize_t(* pwrite)(struct io_handle *handle, const void *buf, size_t nbytes, io_off_t offset)
A pointer to the pwrite method.
Definition: handle.h:99
default.h
io_handle_vtab
The virtual table of an I/O device handle.
Definition: handle.h:74
IO_TYPE_FILE
@ IO_TYPE_FILE
A regular file.
Definition: io.h:49
io_handle::fd
int fd
The native file descriptor.
Definition: handle.h:56
file.h