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