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
43struct file {
51 int flags;
52};
53
54static ssize_t file_read(struct io_handle *handle, void *buf, size_t nbytes);
55static ssize_t file_write(
56 struct io_handle *handle, const void *buf, size_t nbytes);
57static int file_flush(struct io_handle *handle);
58io_off_t file_seek(struct io_handle *handle, io_off_t offset, int whence);
59static ssize_t file_pread(struct io_handle *handle, void *buf, size_t nbytes,
60 io_off_t offset);
61static ssize_t file_pwrite(struct io_handle *handle, const void *buf,
62 size_t nbytes, io_off_t offset);
63
64static 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
74static ssize_t _file_read(struct io_handle *handle, void *buf, size_t nbytes,
75 io_off_t offset);
76static ssize_t _file_write(struct io_handle *handle, const void *buf,
77 size_t nbytes, io_off_t offset);
78#endif
79
81io_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))
98 flags &= ~IO_FILE_TRUNCATE;
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;
135 oflag |= O_EXCL;
136 }
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
170error_alloc_handle:
171#if _WIN32
172 CloseHandle(fd);
173#else
174 close(fd);
175#endif
176error_open:
177error_param:
178 set_errc(errc);
179 return IO_HANDLE_ERROR;
180}
181
182#endif // _WIN32 || _POSIX_C_SOURCE >= 200112L
183
185io_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
202io_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
219io_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
237static ssize_t
238file_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
258static ssize_t
259file_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
286static int
287file_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
305file_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
341static ssize_t
342file_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
368static ssize_t
369file_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
398static 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
437done:
438 CloseHandle(overlapped.hEvent);
439 SetLastError(dwErrCode);
440 return dwNumberOfBytesRead;
441
442error_GetOverlappedResult:
443error_ReadFile:
444 CloseHandle(overlapped.hEvent);
445error_CreateEvent:
446 SetLastError(dwErrCode);
447 return -1;
448}
449
450static 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
490done:
491 CloseHandle(overlapped.hEvent);
492 SetLastError(dwErrCode);
493 return dwNumberOfBytesWritten;
494
495error_GetOverlappedResult:
496error_WriteFile:
497 CloseHandle(overlapped.hEvent);
498error_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
ptrdiff_t ssize_t
Used for a count of bytes or an error indication.
Definition types.h:43