Lely core libraries  2.3.4
frbuf.c
Go to the documentation of this file.
1 
24 #if !_WIN32
25 // This needs to be defined before any files are included to make fstat64(),
26 // lseek64(), mmap64() and pread64() available.
27 #define _LARGEFILE64_SOURCE 1
28 #endif
29 
30 #include "util.h"
31 
32 #if !LELY_NO_STDIO
33 
34 #include <lely/util/errnum.h>
35 #include <lely/util/frbuf.h>
36 
37 #include <assert.h>
38 #include <stdlib.h>
39 
40 #if !_WIN32
41 #include <stdio.h>
42 #if _POSIX_C_SOURCE >= 200112L
43 #include <fcntl.h>
44 #if _POSIX_MAPPED_FILES >= 200112L
45 #include <sys/mman.h>
46 #endif
47 #include <sys/stat.h>
48 #endif
49 #endif
50 
52 struct __frbuf {
53 #if _WIN32
54  HANDLE hFile;
57  HANDLE hFileMappingObject;
59  LPVOID lpBaseAddress;
60 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
61  int fd;
64  void *addr;
66  size_t len;
67 #else
68  FILE *stream;
71  void *map;
72 #endif
73 };
74 
75 void *
76 __frbuf_alloc(void)
77 {
78  void *ptr = malloc(sizeof(struct __frbuf));
79  if (!ptr)
80  set_errc(errno2c(errno));
81  return ptr;
82 }
83 
84 void
85 __frbuf_free(void *ptr)
86 {
87  free(ptr);
88 }
89 
90 struct __frbuf *
91 __frbuf_init(struct __frbuf *buf, const char *filename)
92 {
93  assert(buf);
94  assert(filename);
95 
96 #if _WIN32
97  buf->hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
98  OPEN_EXISTING,
99  FILE_ATTRIBUTE_READONLY | FILE_FLAG_NO_BUFFERING, NULL);
100  if (buf->hFile == INVALID_HANDLE_VALUE)
101  return NULL;
102 
103  buf->hFileMappingObject = INVALID_HANDLE_VALUE;
104  buf->lpBaseAddress = NULL;
105 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
106  buf->fd = open(filename, O_RDONLY | O_CLOEXEC);
107  if (buf->fd == -1)
108  return NULL;
109 
110  buf->addr = MAP_FAILED;
111  buf->len = 0;
112 #else
113  buf->stream = fopen(filename, "rb");
114  if (!buf->stream)
115  return NULL;
116 
117  buf->map = NULL;
118 #endif
119 
120  return buf;
121 }
122 
123 void
124 __frbuf_fini(struct __frbuf *buf)
125 {
126  frbuf_unmap(buf);
127 
128 #if _WIN32
129  CloseHandle(buf->hFile);
130 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
131  close(buf->fd);
132 #else
133  fclose(buf->stream);
134 #endif
135 }
136 
137 frbuf_t *
138 frbuf_create(const char *filename)
139 {
140  int errc = 0;
141 
142  frbuf_t *buf = __frbuf_alloc();
143  if (!buf) {
144  errc = get_errc();
145  goto error_alloc_buf;
146  }
147 
148  if (!__frbuf_init(buf, filename)) {
149  errc = get_errc();
150  goto error_init_buf;
151  }
152 
153  return buf;
154 
155 error_init_buf:
156  __frbuf_free(buf);
157 error_alloc_buf:
158  set_errc(errc);
159  return NULL;
160 }
161 
162 void
164 {
165  if (buf) {
166  __frbuf_fini(buf);
167  __frbuf_free(buf);
168  }
169 }
170 
171 intmax_t
173 {
174  assert(buf);
175 
176 #if _WIN32
177  LARGE_INTEGER FileSize;
178  if (!GetFileSizeEx(buf->hFile, &FileSize))
179  return -1;
180  return FileSize.QuadPart;
181 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
182 #ifdef __linux__
183  struct stat64 stat;
184  if (fstat64(buf->fd, &stat) == -1)
185  return -1;
186 #else
187  struct stat stat;
188  if (fstat(buf->fd, &stat) == -1)
189  return -1;
190 #endif
191  return stat.st_size;
192 #else
193  long offset = ftell(buf->stream);
194  if (offset == -1) {
195  set_errc(errno2c(errno));
196  return -1;
197  }
198 
199  // WARNING: This is not guaranteed to work, but there exists no standard
200  // C alternative.
201  if (fseek(buf->stream, 0, SEEK_END)) {
202  set_errc(errno2c(errno));
203  return -1;
204  }
205 
206  long size = ftell(buf->stream);
207 
208  int errc = get_errc();
209  fseek(buf->stream, offset, SEEK_SET);
210  set_errc(errc);
211 
212  return size;
213 #endif
214 }
215 
216 intmax_t
218 {
219  assert(buf);
220 
221 #if _WIN32
222  LARGE_INTEGER li = { .QuadPart = 0 };
223  if (!SetFilePointerEx(buf->hFile, li, &li, FILE_CURRENT))
224  return -1;
225  return li.QuadPart;
226 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
227 #ifdef __linux__
228  return lseek64(buf->fd, 0, SEEK_CUR);
229 #else
230  return lseek(buf->fd, 0, SEEK_CUR);
231 #endif
232 #else
233  long pos = ftell(buf->stream);
234  if (pos == -1)
235  set_errc(errno2c(errno));
236  return pos;
237 #endif
238 }
239 
240 intmax_t
241 frbuf_set_pos(frbuf_t *buf, intmax_t pos)
242 {
243  assert(buf);
244 
245  if (pos < 0) {
247  return -1;
248  }
249 
250 #if _WIN32
251  LARGE_INTEGER li = { .QuadPart = pos };
252  if (!SetFilePointerEx(buf->hFile, li, &li, FILE_BEGIN))
253  return -1;
254  return li.QuadPart;
255 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
256 #ifdef __linux__
257  return lseek64(buf->fd, pos, SEEK_SET);
258 #else
259  return lseek(buf->fd, pos, SEEK_SET);
260 #endif
261 #else
262  if (pos > LONG_MAX) {
264  return -1;
265  }
266  if (fseek(buf->stream, pos, SEEK_SET)) {
267  set_errc(errno2c(errno));
268  return -1;
269  }
270  return frbuf_get_pos(buf);
271 #endif
272 }
273 
274 ssize_t
275 frbuf_read(frbuf_t *buf, void *ptr, size_t size)
276 {
277  assert(buf);
278  assert(ptr || !size);
279 
280  if (!size)
281  return 0;
282 
283 #if _WIN32
284  DWORD nNumberOfBytesRead;
285  if (!ReadFile(buf->hFile, ptr, size, &nNumberOfBytesRead, NULL))
286  return -1;
287  return nNumberOfBytesRead;
288 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
289  ssize_t result;
290  do
291  result = read(buf->fd, ptr, size);
292  while (result == -1 && errno == EINTR);
293  return result;
294 #else
295  size_t result = fread(ptr, 1, size, buf->stream);
296  if (result != size && ferror(buf->stream)) {
297  set_errc(errno2c(errno));
298  if (!result)
299  return -1;
300  }
301  return result;
302 #endif
303 }
304 
305 ssize_t
306 frbuf_pread(frbuf_t *buf, void *ptr, size_t size, intmax_t pos)
307 {
308  assert(buf);
309  assert(ptr || !size);
310 
311  if (!size)
312  return 0;
313 
314  if (pos < 0) {
316  return -1;
317  }
318 
319 #if _WIN32
320  ssize_t result = 0;
321  DWORD dwErrCode = GetLastError();
322 
323  intmax_t oldpos = frbuf_get_pos(buf);
324  if (oldpos == -1) {
325  result = -1;
326  dwErrCode = GetLastError();
327  goto error_get_pos;
328  }
329 
330  DWORD nNumberOfBytesRead;
331  OVERLAPPED Overlapped = { 0 };
332  ULARGE_INTEGER uli = { .QuadPart = pos };
333  Overlapped.Offset = uli.LowPart;
334  Overlapped.OffsetHigh = uli.HighPart;
335  // clang-format off
336  if (!ReadFile(buf->hFile, ptr, size, &nNumberOfBytesRead,
337  &Overlapped)) {
338  // clang-format on
339  result = -1;
340  dwErrCode = GetLastError();
341  goto error_ReadFile;
342  }
343 
344  result = nNumberOfBytesRead;
345 
346 error_ReadFile:
347  if (frbuf_set_pos(buf, oldpos) == -1 && !dwErrCode)
348  dwErrCode = GetLastError();
349 error_get_pos:
350  SetLastError(dwErrCode);
351  return result;
352 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
353  ssize_t result;
354 #ifdef __linux__
355  do
356  result = pread64(buf->fd, ptr, size, pos);
357 #else
358  do
359  result = pread(buf->fd, ptr, size, pos);
360 #endif
361  while (result == -1 && errno == EINTR);
362  return result;
363 #else
364  ssize_t result = 0;
365  int errc = get_errc();
366 
367  intmax_t oldpos = frbuf_get_pos(buf);
368  if (oldpos == -1) {
369  result = -1;
370  errc = get_errc();
371  goto error_get_pos;
372  }
373 
374  if (frbuf_set_pos(buf, pos) != pos) {
375  result = -1;
376  errc = get_errc();
377  goto error_set_pos;
378  }
379 
380  result = frbuf_read(buf, ptr, size);
381  if (result == -1 || (size_t)result != size)
382  errc = get_errc();
383 
384 error_set_pos:
385  if (frbuf_set_pos(buf, oldpos) == -1 && !errc)
386  errc = get_errc();
387 error_get_pos:
388  set_errc(errc);
389  return result;
390 #endif
391 }
392 
393 const void *
394 frbuf_map(frbuf_t *buf, intmax_t pos, size_t *psize)
395 {
396  frbuf_unmap(buf);
397 
398  intmax_t size = frbuf_get_size(buf);
399  if (size < 0)
400  return NULL;
401  if (pos < 0) {
403  return NULL;
404  }
405  if (pos > (intmax_t)size) {
407  return NULL;
408  }
409  size -= pos;
410 
411  if (psize && *psize)
412  size = MIN((uintmax_t)size, *psize);
413 
414 #if _WIN32
415  DWORD dwErrCode = 0;
416 
417  SYSTEM_INFO SystemInfo;
418  // cppcheck-suppress uninitvar
419  GetSystemInfo(&SystemInfo);
420  DWORD off = pos % SystemInfo.dwAllocationGranularity;
421  if ((uintmax_t)size > (uintmax_t)(SIZE_MAX - off)) {
422  dwErrCode = ERROR_INVALID_PARAMETER;
423  goto error_size;
424  }
425 
426  ULARGE_INTEGER MaximumSize = { .QuadPart = pos + size };
427  buf->hFileMappingObject = CreateFileMapping(buf->hFile, NULL,
428  PAGE_READONLY, MaximumSize.HighPart,
429  MaximumSize.LowPart, NULL);
430  if (buf->hFileMappingObject == INVALID_HANDLE_VALUE) {
431  dwErrCode = GetLastError();
432  goto error_CreateFileMapping;
433  }
434 
435  ULARGE_INTEGER FileOffset = { .QuadPart = pos - off };
436  buf->lpBaseAddress = MapViewOfFile(buf->hFileMappingObject,
437  FILE_MAP_READ, FileOffset.HighPart, FileOffset.LowPart,
438  (SIZE_T)(off + size));
439  if (!buf->lpBaseAddress) {
440  dwErrCode = GetLastError();
441  goto error_MapViewOfFile;
442  }
443 
444  if (psize)
445  *psize = (size_t)size;
446 
447  return (char *)buf->lpBaseAddress + off;
448 
449 error_MapViewOfFile:
450  CloseHandle(buf->hFileMappingObject);
451  buf->hFileMappingObject = INVALID_HANDLE_VALUE;
452 error_CreateFileMapping:
453 error_size:
454  SetLastError(dwErrCode);
455  return NULL;
456 #elif _POSIX_MAPPED_FILES >= 200112L
457  long page_size = sysconf(_SC_PAGE_SIZE);
458  if (page_size <= 0)
459  return NULL;
460  intmax_t off = pos % page_size;
461  if ((uintmax_t)size > (uintmax_t)(SIZE_MAX - off)) {
462  errno = EOVERFLOW;
463  return NULL;
464  }
465 
466 #ifdef __linux__
467  buf->addr = mmap64(NULL, off + size, PROT_READ, MAP_SHARED, buf->fd,
468  pos - off);
469 #else
470  // TODO: Check if `pos - off` does not overflow the range of off_t.
471  buf->addr = mmap(NULL, off + size, PROT_READ, MAP_SHARED, buf->fd,
472  pos - off);
473 #endif
474  if (buf->addr == MAP_FAILED)
475  return NULL;
476  buf->len = off + size;
477 
478  if (psize)
479  *psize = size;
480 
481  return (char *)buf->addr + off;
482 #else
483  int errc = get_errc();
484 
485  buf->map = malloc(size);
486  if (!buf->map) {
487  errc = errno2c(errno);
488  goto error_malloc_map;
489  }
490 
491  ssize_t result = frbuf_pread(buf, buf->map, size, pos);
492  if (result == -1) {
493  errc = get_errc();
494  goto error_pread;
495  }
496  size = result;
497 
498  if (psize)
499  *psize = size;
500 
501  return buf->map;
502 
503 error_pread:
504  free(buf->map);
505  buf->map = NULL;
506 error_malloc_map:
507  set_errc(errc);
508  return NULL;
509 #endif
510 }
511 
512 int
514 {
515  assert(buf);
516 
517  int result = 0;
518 
519 #if _WIN32
520  if (buf->hFileMappingObject != INVALID_HANDLE_VALUE) {
521  DWORD dwErrCode = GetLastError();
522  if (!UnmapViewOfFile(buf->lpBaseAddress)) {
523  result = -1;
524  dwErrCode = GetLastError();
525  }
526  if (!CloseHandle(buf->hFileMappingObject) && !result) {
527  result = -1;
528  dwErrCode = GetLastError();
529  }
530  SetLastError(dwErrCode);
531 
532  buf->hFileMappingObject = INVALID_HANDLE_VALUE;
533  buf->lpBaseAddress = NULL;
534  }
535 #elif _POSIX_MAPPED_FILES >= 200112L
536  if (buf->addr != MAP_FAILED) {
537  result = munmap(buf->addr, buf->len);
538 
539  buf->addr = MAP_FAILED;
540  buf->len = 0;
541  }
542 #else
543  if (buf->map) {
544  free(buf->map);
545 
546  buf->map = NULL;
547  }
548 #endif
549 
550  return result;
551 }
552 
553 #endif // !LELY_NO_STDIO
frbuf_create
frbuf_t * frbuf_create(const char *filename)
Creates a new read file buffer.
Definition: frbuf.c:138
frbuf_read
ssize_t frbuf_read(frbuf_t *buf, void *ptr, size_t size)
Reads bytes from the current position in a read file buffer.
Definition: frbuf.c:275
MIN
#define MIN(a, b)
Returns the minimum of a and b.
Definition: util.h:57
frbuf_set_pos
intmax_t frbuf_set_pos(frbuf_t *buf, intmax_t pos)
Sets the current offset (in bytes) of a read file buffer with respect to the beginning of the file.
Definition: frbuf.c:241
frbuf_get_size
intmax_t frbuf_get_size(frbuf_t *buf)
Returns the size (in bytes) of the a read file buffer, or -1 on error.
Definition: frbuf.c:172
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:932
errno2c
int errno2c(int errnum)
Transforms a standard C error number to a native error code.
Definition: errnum.c:46
frbuf_pread
ssize_t frbuf_pread(frbuf_t *buf, void *ptr, size_t size, intmax_t pos)
Reads bytes from the specified position in a read file buffer.
Definition: frbuf.c:306
util.h
frbuf_map
const void * frbuf_map(frbuf_t *buf, intmax_t pos, size_t *psize)
Maps (part of) the contents of a read file buffer to memory.
Definition: frbuf.c:394
set_errnum
void set_errnum(errnum_t errnum)
Sets the current (thread-specific) platform-independent error number to errnum.
Definition: errnum.h:424
set_errc
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:944
errnum.h
__frbuf::fd
int fd
The file descriptor.
Definition: frbuf.c:62
ERRNUM_INVAL
@ ERRNUM_INVAL
Invalid argument.
Definition: errnum.h:132
stdio.h
__frbuf
An read file buffer struct.
Definition: frbuf.c:52
frbuf_destroy
void frbuf_destroy(frbuf_t *buf)
Destroys a read file buffer.
Definition: frbuf.c:163
__frbuf::len
size_t len
The length (in bytes) of the mapping at addr.
Definition: frbuf.c:66
frbuf_get_pos
intmax_t frbuf_get_pos(frbuf_t *buf)
Returns the current offset (in bytes) of a read file buffer with respect to the beginning of the file...
Definition: frbuf.c:217
__frbuf::addr
void * addr
The base address of the current file mapping.
Definition: frbuf.c:64
frbuf.h
ERRNUM_OVERFLOW
@ ERRNUM_OVERFLOW
Value too large to be stored in data type.
Definition: errnum.h:204
stdlib.h
frbuf_unmap
int frbuf_unmap(frbuf_t *buf)
Unmaps the current memory map of a read file buffer, if it exists.
Definition: frbuf.c:513