Lely core libraries 1.9.2
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
39struct file {
47 int flags;
48};
49
50static ssize_t file_read(struct io_handle *handle, void *buf, size_t nbytes);
51static ssize_t file_write(
52 struct io_handle *handle, const void *buf, size_t nbytes);
53static int file_flush(struct io_handle *handle);
54io_off_t file_seek(struct io_handle *handle, io_off_t offset, int whence);
55static ssize_t file_pread(struct io_handle *handle, void *buf, size_t nbytes,
56 io_off_t offset);
57static ssize_t file_pwrite(struct io_handle *handle, const void *buf,
58 size_t nbytes, io_off_t offset);
59
60static 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
70static ssize_t _file_read(struct io_handle *handle, void *buf, size_t nbytes,
71 io_off_t offset);
72static ssize_t _file_write(struct io_handle *handle, const void *buf,
73 size_t nbytes, io_off_t offset);
74#endif
75
77io_open_file(const char *path, int flags)
78{
79 assert(path);
80
81 int errc = 0;
82
84 errc = errnum2c(ERRNUM_INVAL);
85 goto error_param;
86 }
87
88 if (!(flags & IO_FILE_WRITE))
91 if (!(flags & IO_FILE_CREATE))
94 flags &= ~IO_FILE_TRUNCATE;
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;
131 oflag |= O_EXCL;
132 }
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 (__unlikely(fd == -1 && errno == EINTR));
148#endif
149
150 if (__unlikely(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 (__unlikely(!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
166error_alloc_handle:
167#ifdef _WIN32
168 CloseHandle(fd);
169#else
170 close(fd);
171#endif
172error_open:
173error_param:
174 set_errc(errc);
175 return IO_HANDLE_ERROR;
176}
177
178#endif // _WIN32 || _POSIX_C_SOURCE >= 200112L
179
181io_seek(io_handle_t handle, io_off_t offset, int whence)
182{
183 if (__unlikely(handle == IO_HANDLE_ERROR)) {
185 return -1;
186 }
187
188 assert(handle->vtab);
189 if (__unlikely(!handle->vtab->seek)) {
191 return -1;
192 }
193
194 return handle->vtab->seek(handle, offset, whence);
195}
196
198io_pread(io_handle_t handle, void *buf, size_t nbytes, io_off_t offset)
199{
200 if (__unlikely(handle == IO_HANDLE_ERROR)) {
202 return -1;
203 }
204
205 assert(handle->vtab);
206 if (__unlikely(!handle->vtab->pread)) {
208 return -1;
209 }
210
211 return handle->vtab->pread(handle, buf, nbytes, offset);
212}
213
215io_pwrite(io_handle_t handle, const void *buf, size_t nbytes, io_off_t offset)
216{
217 if (__unlikely(handle == IO_HANDLE_ERROR)) {
219 return -1;
220 }
221
222 assert(handle->vtab);
223 if (__unlikely(!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
233static ssize_t
234file_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 (__unlikely(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 (__unlikely(result == -1 && errno == EINTR));
250 return result;
251#endif
252}
253
254static ssize_t
255file_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 (__unlikely(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 (__unlikely(result == -1 && errno == EINTR));
278 return result;
279#endif
280}
281
282static int
283file_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 (__unlikely(result == -1 && errno == EINTR));
296 return result;
297#endif
298}
299
301file_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 (__unlikely(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
337static ssize_t
338file_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 (__unlikely(current == -1))
345 return -1;
346 ssize_t result = _file_read(handle, buf, nbytes, offset);
347 if (__unlikely(result == -1))
348 return -1;
349 // pread() does not change the file pointer.
350 if (__unlikely(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 (__unlikely(result == -1 && errno == EINTR));
360 return result;
361#endif
362}
363
364static ssize_t
365file_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 (__unlikely(current == -1))
373 return -1;
374 ssize_t result = _file_write(handle, buf, nbytes, offset);
375 if (__unlikely(result == -1))
376 return -1;
377 // pwrite() does not change the file pointer.
378 if (__unlikely(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 (__unlikely(result == -1 && errno == EINTR));
388 return result;
389#endif
390}
391
392#ifdef _WIN32
393
394static 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 (__unlikely(!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 (__unlikely(GetLastError() != ERROR_IO_PENDING)) {
421 dwErrCode = GetLastError();
422 goto error_ReadFile;
423 }
424
425 // clang-format off
426 if (__unlikely(!GetOverlappedResult(handle->fd, &overlapped,
427 &dwNumberOfBytesRead, TRUE))) {
428 // clang-format on
429 dwErrCode = GetLastError();
430 goto error_GetOverlappedResult;
431 }
432
433done:
434 CloseHandle(overlapped.hEvent);
435 SetLastError(dwErrCode);
436 return dwNumberOfBytesRead;
437
438error_GetOverlappedResult:
439error_ReadFile:
440 CloseHandle(overlapped.hEvent);
441error_CreateEvent:
442 SetLastError(dwErrCode);
443 return -1;
444}
445
446static 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 (__unlikely(!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 (__unlikely(GetLastError() != ERROR_IO_PENDING)) {
474 dwErrCode = GetLastError();
475 goto error_WriteFile;
476 }
477
478 // clang-format off
479 if (__unlikely(!GetOverlappedResult(handle->fd, &overlapped,
480 &dwNumberOfBytesWritten, TRUE))) {
481 // clang-format on
482 dwErrCode = GetLastError();
483 goto error_GetOverlappedResult;
484 }
485
486done:
487 CloseHandle(overlapped.hEvent);
488 SetLastError(dwErrCode);
489 return dwNumberOfBytesWritten;
490
491error_GetOverlappedResult:
492error_WriteFile:
493 CloseHandle(overlapped.hEvent);
494error_CreateEvent:
495 SetLastError(dwErrCode);
496 return -1;
497}
498
499#endif // _WIN32
500
501#endif // _WIN32 || _POSIX_C_SOURCE >= 200112L
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:825
@ ERRNUM_SPIPE
Invalid seek.
Definition: errnum.h:219
@ ERRNUM_BADF
Bad file descriptor.
Definition: errnum.h:90
@ ERRNUM_INVAL
Invalid argument.
Definition: errnum.h:129
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function.
Definition: errnum.c:947
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:957
void set_errnum(errnum_t errnum)
Sets the current (thread-specific) platform-independent error number to errnum.
Definition: errnum.h:375
#define __unlikely(x)
Indicates to the compiler that the expression is most-likely false.
Definition: features.h:286
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_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
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_t io_open_file(const char *path, int flags)
Opens a regular file.
Definition: file.c:77
This header file is part of the I/O library; it contains the regular file declarations.
@ 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
@ 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
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
@ IO_TYPE_FILE
A regular file.
Definition: io.h:49
int64_t io_off_t
A file offset type.
Definition: io.h:37
io_handle_t io_handle_acquire(io_handle_t handle)
Increments the reference count of an I/O device handle.
Definition: handle.c:32
#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 I/O library.
A regular file handle.
Definition: file.c:39
struct io_handle base
The I/O device base handle.
Definition: file.c:41
int flags
The file flags (any combination of IO_FILE_READ, IO_FILE_WRITE, IO_FILE_APPEND, IO_FILE_CREATE,...
Definition: file.c:47
The virtual table of an I/O device handle.
Definition: handle.h:74
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
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
io_off_t(* seek)(struct io_handle *handle, io_off_t offset, int whence)
A pointer to the seek method.
Definition: handle.h:94
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
An I/O device handle.
Definition: handle.h:41
int fd
The native file descriptor.
Definition: handle.h:56
const struct io_handle_vtab * vtab
A pointer to the virtual table.
Definition: handle.h:43
ptrdiff_t ssize_t
Used for a count of bytes or an error indication.
Definition: types.h:43