Lely core libraries  2.3.4
fwbuf.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/libc/string.h>
35 #include <lely/util/errnum.h>
36 #include <lely/util/fwbuf.h>
37 
38 #include <assert.h>
39 #include <stdlib.h>
40 
41 #if _WIN32
42 #ifdef _MSC_VER
43 #pragma comment(lib, "shlwapi.lib")
44 #endif
45 #include <shlwapi.h>
46 #else
47 #include <stdio.h>
48 #if _POSIX_C_SOURCE >= 200112L
49 #include <fcntl.h>
50 #include <libgen.h>
51 #if _POSIX_MAPPED_FILES >= 200112L
52 #include <sys/mman.h>
53 #endif
54 #include <sys/stat.h>
55 #endif
56 #endif
57 
59 struct __fwbuf {
61  char *filename;
62 #if _WIN32
64  char tmpname[MAX_PATH];
66  HANDLE hFile;
68  DWORD dwErrCode;
70  HANDLE hFileMappingObject;
72  LPVOID lpBaseAddress;
74  SIZE_T dwNumberOfBytesToMap;
75 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
77  char *tmpname;
79  int dirfd;
81  int fd;
83  int errsv;
85  void *addr;
87  size_t len;
88 #else
90  char tmpname[L_tmpnam];
92  FILE *stream;
94  int errnum;
96  intmax_t size;
98  intmax_t last;
100  void *map;
105  intmax_t pos;
107  size_t len;
108 #endif
109 };
110 
111 void *
112 __fwbuf_alloc(void)
113 {
114  void *ptr = malloc(sizeof(struct __fwbuf));
115  if (!ptr)
116  set_errc(errno2c(errno));
117  return ptr;
118 }
119 
120 void
121 __fwbuf_free(void *ptr)
122 {
123  free(ptr);
124 }
125 
126 struct __fwbuf *
127 __fwbuf_init(struct __fwbuf *buf, const char *filename)
128 {
129  assert(buf);
130  assert(filename);
131 
132 #if _WIN32
133  DWORD dwErrCode = 0;
134 
135  buf->filename = _strdup(filename);
136  if (!buf->filename) {
137  dwErrCode = errno2c(errno);
138  goto error_strdup;
139  }
140 
141  // Obtain the directory name.
142  char dir[MAX_PATH - 14];
143  strncpy(dir, buf->filename, MAX_PATH - 14 - 1);
144  dir[MAX_PATH - 14 - 1] = '\0';
145  PathRemoveFileSpecA(dir);
146  if (!*dir) {
147  dir[0] = '.';
148  dir[1] = '\0';
149  }
150 
151  if (!GetTempFileNameA(dir, "tmp", 0, buf->tmpname)) {
152  dwErrCode = GetLastError();
153  goto error_GetTempFileNameA;
154  }
155 
156  buf->hFile = CreateFileA(buf->tmpname, GENERIC_READ | GENERIC_WRITE, 0,
157  NULL, TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
158  if (buf->hFile == INVALID_HANDLE_VALUE) {
159  dwErrCode = GetLastError();
160  goto error_CreateFileA;
161  }
162 
163  buf->dwErrCode = 0;
164 
165  buf->hFileMappingObject = INVALID_HANDLE_VALUE;
166  buf->lpBaseAddress = NULL;
167  buf->dwNumberOfBytesToMap = 0;
168 
169  return buf;
170 
171 error_CreateFileA:
172  DeleteFileA(buf->tmpname);
173 error_GetTempFileNameA:
174  free(buf->filename);
175 error_strdup:
176  SetLastError(dwErrCode);
177  return NULL;
178 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
179  int errsv = 0;
180 
181  buf->filename = strdup(filename);
182  if (!buf->filename) {
183  errsv = errno;
184  goto error_strdup_filename;
185  }
186 
187  char *tmp = strdup(buf->filename);
188  if (!tmp) {
189  errsv = errno;
190  goto error_strdup_tmp;
191  }
192  char *dir = dirname(tmp);
193  size_t n = strlen(dir);
194 
195  buf->dirfd = open(dir, O_RDONLY | O_CLOEXEC | O_DIRECTORY);
196  if (buf->dirfd == -1) {
197  errsv = errno;
198  goto error_open_dirfd;
199  }
200 
201  buf->tmpname = malloc(n + 13);
202  if (!buf->tmpname) {
203  errsv = errno;
204  goto error_malloc_tmpname;
205  }
206 
207  strcpy(buf->tmpname, dir);
208  if (!n || buf->tmpname[n - 1] != '/')
209  strcat(buf->tmpname, "/.tmp-XXXXXX");
210  else
211  strcat(buf->tmpname, ".tmp-XXXXXX");
212 
213  free(tmp);
214  tmp = NULL;
215 
216 #ifdef _GNU_SOURCE
217  buf->fd = mkostemp(buf->tmpname, O_CLOEXEC);
218 #else
219  buf->fd = mkstemp(buf->tmpname);
220 #endif
221  if (buf->fd == -1) {
222  errsv = errno;
223  goto error_open_fd;
224  }
225 
226 #ifndef _GNU_SOURCE
227  if (fcntl(buf->fd, F_SETFD, FD_CLOEXEC) == -1) {
228  errsv = errno;
229  goto error_fcntl;
230  }
231 #endif
232 
233  buf->errsv = 0;
234 
235  buf->addr = MAP_FAILED;
236  buf->len = 0;
237 
238  return buf;
239 
240 #ifndef _GNU_SOURCE
241 error_fcntl:
242 #endif
243 error_open_fd:
244  free(buf->tmpname);
245 error_malloc_tmpname:
246  close(buf->dirfd);
247 error_open_dirfd:
248  if (tmp)
249  free(tmp);
250 error_strdup_tmp:
251  free(buf->filename);
252 error_strdup_filename:
253  errno = errsv;
254  return NULL;
255 #else
256  int errc = 0;
257 
258  buf->filename = strdup(filename);
259  if (!buf->filename) {
260  errc = errno2c(errno);
261  goto error_strdup;
262  }
263 
264  // cppcheck-suppress tmpnamCalled
265  buf->stream = fopen(tmpnam(buf->tmpname), "w+b");
266  if (!buf->stream) {
267  errc = errno2c(errno);
268  goto error_fopen;
269  }
270 
271  buf->errnum = 0;
272 
273  buf->size = 0;
274  buf->last = 0;
275 
276  buf->map = NULL;
277  buf->pos = 0;
278  buf->len = 0;
279 
280  return buf;
281 
282 error_fopen:
283  free(buf->filename);
284 error_strdup:
285  set_errc(errc);
286  return NULL;
287 #endif
288 }
289 
290 void
291 __fwbuf_fini(struct __fwbuf *buf)
292 {
293  int errc = get_errc();
294  fwbuf_cancel(buf);
295  fwbuf_commit(buf);
296  set_errc(errc);
297 
298 #if _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
299  free(buf->tmpname);
300 #endif
301  free(buf->filename);
302 }
303 
304 fwbuf_t *
305 fwbuf_create(const char *filename)
306 {
307  int errc = 0;
308 
309  fwbuf_t *buf = __fwbuf_alloc();
310  if (!buf) {
311  errc = get_errc();
312  goto error_alloc_buf;
313  }
314 
315  if (!__fwbuf_init(buf, filename)) {
316  errc = get_errc();
317  goto error_init_buf;
318  }
319 
320  return buf;
321 
322 error_init_buf:
323  __fwbuf_free(buf);
324 error_alloc_buf:
325  set_errc(errc);
326  return NULL;
327 }
328 
329 void
331 {
332  if (buf) {
333  __fwbuf_fini(buf);
334  __fwbuf_free(buf);
335  }
336 }
337 
338 intmax_t
340 {
341  if (fwbuf_error(buf))
342  return -1;
343 
344 #if _WIN32
345  LARGE_INTEGER FileSize;
346  if (!GetFileSizeEx(buf->hFile, &FileSize)) {
347  buf->dwErrCode = GetLastError();
348  return -1;
349  }
350  return FileSize.QuadPart;
351 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
352 #ifdef __linux__
353  struct stat64 stat;
354  if (fstat64(buf->fd, &stat) == -1) {
355 #else
356  struct stat stat;
357  if (fstat(buf->fd, &stat) == -1) {
358 #endif
359  buf->errsv = errno;
360  return -1;
361  }
362  return stat.st_size;
363 #else
364  return buf->size;
365 #endif
366 }
367 
368 int
369 fwbuf_set_size(fwbuf_t *buf, intmax_t size)
370 {
371  if (fwbuf_unmap(buf) == -1)
372  return -1;
373 
374 #if _WIN32
375  intmax_t pos = fwbuf_get_pos(buf);
376  if (pos == -1)
377  return -1;
378 
379  if (fwbuf_set_pos(buf, size) == -1)
380  return -1;
381 
382  if (!SetEndOfFile(buf->hFile)) {
383  buf->dwErrCode = GetLastError();
384  return -1;
385  }
386 
387  if (fwbuf_set_pos(buf, pos) == -1)
388  return -1;
389 
390  return 0;
391 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
392 #ifdef __linux__
393  if (ftruncate64(buf->fd, size) == -1) {
394 #else
395  // TODO: Check if size does not overflow the range of off_t.
396  if (ftruncate(buf->fd, size) == -1) {
397 #endif
398  buf->errsv = errno;
399  return -1;
400  }
401  return 0;
402 #else
403  if (size < buf->last) {
404  set_errnum(buf->errnum = ERRNUM_INVAL);
405  return -1;
406  }
407 
408  buf->size = size;
409 
410  return 0;
411 #endif
412 }
413 
414 intmax_t
416 {
417  if (fwbuf_error(buf))
418  return -1;
419 
420 #if _WIN32
421  LARGE_INTEGER li = { .QuadPart = 0 };
422  if (!SetFilePointerEx(buf->hFile, li, &li, FILE_CURRENT)) {
423  buf->dwErrCode = GetLastError();
424  return -1;
425  }
426  return li.QuadPart;
427 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
428 #ifdef __linux__
429  intmax_t pos = lseek64(buf->fd, 0, SEEK_CUR);
430 #else
431  intmax_t pos = lseek(buf->fd, 0, SEEK_CUR);
432 #endif
433  if (pos == -1)
434  buf->errsv = errno;
435  return pos;
436 #else
437  long pos = ftell(buf->stream);
438  if (pos == -1) {
439  buf->errnum = errno2num(errno);
440  set_errc(errno2c(errno));
441  }
442  return pos;
443 #endif
444 }
445 
446 intmax_t
447 fwbuf_set_pos(fwbuf_t *buf, intmax_t pos)
448 {
449  if (fwbuf_error(buf))
450  return -1;
451 
452 #if _WIN32
453  LARGE_INTEGER li = { .QuadPart = pos };
454  if (!SetFilePointerEx(buf->hFile, li, &li, FILE_BEGIN)) {
455  buf->dwErrCode = GetLastError();
456  return -1;
457  }
458  return li.QuadPart;
459 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
460 #ifdef __linux__
461  pos = lseek64(buf->fd, pos, SEEK_SET);
462 #else
463  pos = lseek(buf->fd, pos, SEEK_SET);
464 #endif
465  if (pos == -1)
466  buf->errsv = errno;
467  return pos;
468 #else
469  if (pos < 0) {
470  set_errnum(buf->errnum = ERRNUM_INVAL);
471  return -1;
472  }
473  if (pos > LONG_MAX) {
474  set_errnum(buf->errnum = ERRNUM_OVERFLOW);
475  return -1;
476  }
477 
478  if (fseek(buf->stream, pos, SEEK_SET)) {
479  buf->errnum = errno2num(errno);
480  set_errc(errno2c(errno));
481  return -1;
482  }
483 
484  return fwbuf_get_pos(buf);
485 #endif
486 }
487 
488 ssize_t
489 fwbuf_write(fwbuf_t *buf, const void *ptr, size_t size)
490 {
491  assert(ptr || !size);
492 
493  if (fwbuf_error(buf))
494  return -1;
495 
496  if (!size)
497  return 0;
498 
499 #if _WIN32
500  DWORD nNumberOfBytesWritten;
501  if (!WriteFile(buf->hFile, ptr, size, &nNumberOfBytesWritten, NULL)) {
502  buf->dwErrCode = GetLastError();
503  return -1;
504  }
505  return nNumberOfBytesWritten;
506 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
507  ssize_t result;
508  do
509  result = write(buf->fd, ptr, size);
510  while (result == -1 && errno == EINTR);
511  if (result == -1)
512  buf->errsv = errno;
513  return result;
514 #else
515  intmax_t pos = fwbuf_get_pos(buf);
516  if (pos < 0)
517  return -1;
518 
519  size_t result = fwrite(ptr, 1, size, buf->stream);
520  if (result != size && ferror(buf->stream)) {
521  buf->errnum = errno2num(errno);
522  set_errc(errno2c(errno));
523  if (!result)
524  return -1;
525  }
526 
527  // Update the memory map, if necessary.
528  if (buf->map && pos < buf->pos + (intmax_t)buf->len
529  && pos + (intmax_t)size > buf->pos) {
530  size_t begin = MAX(pos - buf->pos, 0);
531  size_t end = MIN(pos + size - buf->pos, buf->len);
532  memmove((char *)buf->map + begin, ptr, end - begin);
533  }
534 
535  buf->last = MAX(buf->last, pos + (intmax_t)result);
536  buf->size = MAX(buf->size, buf->last);
537 
538  return result;
539 #endif
540 }
541 
542 ssize_t
543 fwbuf_pwrite(fwbuf_t *buf, const void *ptr, size_t size, intmax_t pos)
544 {
545  assert(ptr || !size);
546 
547  if (fwbuf_error(buf))
548  return -1;
549 
550  if (!size)
551  return 0;
552 
553 #if _WIN32
554  ssize_t result = 0;
555 
556  if (pos < 0) {
557  result = -1;
558  buf->dwErrCode = ERROR_INVALID_PARAMETER;
559  goto error_pos;
560  }
561 
562  intmax_t oldpos = fwbuf_get_pos(buf);
563  if (oldpos == -1) {
564  result = -1;
565  goto error_get_pos;
566  }
567 
568  DWORD nNumberOfBytesWritten;
569  OVERLAPPED Overlapped = { 0 };
570  ULARGE_INTEGER uli = { .QuadPart = pos };
571  Overlapped.Offset = uli.LowPart;
572  Overlapped.OffsetHigh = uli.HighPart;
573  // clang-format off
574  if (!WriteFile(buf->hFile, ptr, size, &nNumberOfBytesWritten,
575  &Overlapped)) {
576  // clang-format on
577  result = -1;
578  buf->dwErrCode = GetLastError();
579  goto error_WriteFile;
580  }
581 
582  result = nNumberOfBytesWritten;
583 
584 error_WriteFile:
585  fwbuf_set_pos(buf, oldpos);
586 error_get_pos:
587 error_pos:
588  SetLastError(buf->dwErrCode);
589  return result;
590 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
591  ssize_t result;
592 #ifdef __linux__
593  do
594  result = pwrite64(buf->fd, ptr, size, pos);
595 #else
596  do
597  result = pwrite(buf->fd, ptr, size, pos);
598 #endif
599  while (result == -1 && errno == EINTR);
600  if (result == -1)
601  buf->errsv = errno;
602  return result;
603 #else
604  ssize_t result = 0;
605  int errc = get_errc();
606 
607  if (pos < 0) {
608  result = -1;
609  errc = errnum2c(buf->errnum = ERRNUM_INVAL);
610  goto error_pos;
611  }
612 
613  intmax_t oldpos = fwbuf_get_pos(buf);
614  if (oldpos == -1) {
615  result = -1;
616  errc = get_errc();
617  goto error_get_pos;
618  }
619 
620  // Move to the requested position and fill any gap with zeros.
621  if (buf->last < pos) {
622  if (fwbuf_set_pos(buf, buf->last) != buf->last) {
623  errc = get_errc();
624  goto error_set_pos;
625  }
626  while (buf->last < pos) {
627  if (fputc(0, buf->stream) == EOF) {
628  buf->errnum = errno2num(errno);
629  errc = errno2c(errno);
630  goto error_fputc;
631  }
632  buf->last++;
633  buf->size = MAX(buf->size, buf->last);
634  }
635  } else {
636  if (fwbuf_set_pos(buf, pos) != pos) {
637  errc = get_errc();
638  goto error_set_pos;
639  }
640  }
641 
642  result = fwbuf_write(buf, ptr, size);
643  if (result == -1 || (size_t)result != size)
644  errc = get_errc();
645 
646 error_fputc:
647 error_set_pos:
648  if (fwbuf_set_pos(buf, oldpos) == -1 && !errc)
649  errc = get_errc();
650 error_get_pos:
651 error_pos:
652  set_errc(errc);
653  return result;
654 #endif
655 }
656 
657 void *
658 fwbuf_map(fwbuf_t *buf, intmax_t pos, size_t *psize)
659 {
660  if (fwbuf_unmap(buf) == -1)
661  return NULL;
662 
663  intmax_t size = fwbuf_get_size(buf);
664  if (size < 0)
665  return NULL;
666  if (pos < 0) {
667 #if _WIN32
668  SetLastError(buf->dwErrCode = ERROR_INVALID_PARAMETER);
669 #elif _POSIX_MAPPED_FILES >= 200112L
670  errno = buf->errsv = EINVAL;
671 #else
672  set_errnum(buf->errnum = ERRNUM_INVAL);
673 #endif
674  return NULL;
675  }
676  if (pos > (intmax_t)size) {
677 #if _WIN32
678  SetLastError(buf->dwErrCode = ERROR_INVALID_PARAMETER);
679 #elif _POSIX_MAPPED_FILES >= 200112L
680  errno = buf->errsv = EOVERFLOW;
681 #else
682  set_errnum(buf->errnum = ERRNUM_OVERFLOW);
683 #endif
684  return NULL;
685  }
686  size -= pos;
687 
688  if (psize && *psize)
689  size = MIN((uintmax_t)size, *psize);
690 
691 #if _WIN32
692  SYSTEM_INFO SystemInfo;
693  // cppcheck-suppress uninitvar
694  GetSystemInfo(&SystemInfo);
695  DWORD off = pos % SystemInfo.dwAllocationGranularity;
696  if ((uintmax_t)size > (uintmax_t)(SIZE_MAX - off)) {
697  buf->dwErrCode = ERROR_INVALID_PARAMETER;
698  goto error_size;
699  }
700 
701  ULARGE_INTEGER MaximumSize = { .QuadPart = pos + size };
702  buf->hFileMappingObject = CreateFileMapping(buf->hFile, NULL,
703  PAGE_READWRITE, MaximumSize.HighPart,
704  MaximumSize.LowPart, NULL);
705  if (buf->hFileMappingObject == INVALID_HANDLE_VALUE) {
706  buf->dwErrCode = GetLastError();
707  goto error_CreateFileMapping;
708  }
709 
710  ULARGE_INTEGER FileOffset = { .QuadPart = pos - off };
711  buf->lpBaseAddress = MapViewOfFile(buf->hFileMappingObject,
712  FILE_MAP_WRITE, FileOffset.HighPart, FileOffset.LowPart,
713  (SIZE_T)(off + size));
714  if (!buf->lpBaseAddress) {
715  buf->dwErrCode = GetLastError();
716  goto error_MapViewOfFile;
717  }
718 
719  if (psize)
720  *psize = (size_t)size;
721 
722  return (char *)buf->lpBaseAddress + off;
723 
724 error_MapViewOfFile:
725  CloseHandle(buf->hFileMappingObject);
726  buf->hFileMappingObject = INVALID_HANDLE_VALUE;
727 error_CreateFileMapping:
728 error_size:
729  SetLastError(buf->dwErrCode);
730  return NULL;
731 #elif _POSIX_MAPPED_FILES >= 200112L
732  long page_size = sysconf(_SC_PAGE_SIZE);
733  if (page_size <= 0) {
734  buf->errsv = errno;
735  return NULL;
736  }
737  intmax_t off = pos % page_size;
738  if ((uintmax_t)size > (uintmax_t)(SIZE_MAX - off)) {
739  errno = buf->errsv = EOVERFLOW;
740  return NULL;
741  }
742 
743 #ifdef __linux__
744  buf->addr = mmap64(NULL, off + size, PROT_READ | PROT_WRITE, MAP_SHARED,
745  buf->fd, pos - off);
746 #else
747  // TODO: Check if `pos - off` does not overflow the range of off_t.
748  buf->addr = mmap(NULL, off + size, PROT_READ | PROT_WRITE, MAP_SHARED,
749  buf->fd, pos - off);
750 #endif
751  if (buf->addr == MAP_FAILED) {
752  buf->errsv = errno;
753  return NULL;
754  }
755  buf->len = off + size;
756 
757  if (psize)
758  *psize = size;
759 
760  return (char *)buf->addr + off;
761 #else
762  int errc = 0;
763 
764  if ((uintmax_t)size > SIZE_MAX) {
765  set_errnum(buf->errnum = ERRNUM_OVERFLOW);
766  return NULL;
767  }
768 
769  buf->map = calloc(size, 1);
770  if (!buf->map) {
771  buf->errnum = errno2num(errno);
772  errc = errno2c(errno);
773  goto error_malloc_map;
774  }
775 
776  // Copy bytes that have been written to the file to the mapped memory
777  // region.
778  intmax_t oldpos = 0;
779  if (pos < buf->last) {
780  oldpos = fwbuf_get_pos(buf);
781  if (oldpos == -1) {
782  errc = get_errc();
783  goto error_get_pos;
784  }
785 
786  if (fwbuf_set_pos(buf, pos) != pos) {
787  errc = get_errc();
788  goto error_set_pos;
789  }
790 
791  size_t nitems = MIN(size, buf->last - pos);
792  if (fread(buf->map, 1, nitems, buf->stream) != nitems
793  && ferror(buf->stream)) {
794  buf->errnum = errno2num(errno);
795  errc = errno2c(errno);
796  goto error_fread;
797  }
798 
799  if (fwbuf_set_pos(buf, oldpos) == oldpos) {
800  errc = get_errc();
801  goto error_set_pos;
802  }
803  }
804 
805  buf->pos = pos;
806  buf->len = size;
807 
808  if (psize)
809  *psize = size;
810 
811  return buf->map;
812 
813 error_fread:
814 error_set_pos:
815  if (fwbuf_set_pos(buf, oldpos) == -1 && !errc)
816  errc = get_errc();
817 error_get_pos:
818  free(buf->map);
819  buf->map = NULL;
820 error_malloc_map:
821  set_errc(errc);
822  return NULL;
823 #endif
824 }
825 
826 int
828 {
829  assert(buf);
830 
831  int result = 0;
832 #if _WIN32
833  DWORD dwErrCode = 0;
834  if (buf->dwErrCode) {
835  result = -1;
836  dwErrCode = buf->dwErrCode;
837  }
838 
839  if (buf->hFileMappingObject != INVALID_HANDLE_VALUE) {
840  // clang-format off
841  if (!FlushViewOfFile(buf->lpBaseAddress,
842  buf->dwNumberOfBytesToMap) && !result) {
843  // clang-format on
844  result = -1;
845  dwErrCode = GetLastError();
846  }
847  if (!UnmapViewOfFile(buf->lpBaseAddress) && !result) {
848  result = -1;
849  dwErrCode = GetLastError();
850  }
851  if (!CloseHandle(buf->hFileMappingObject) && !result) {
852  result = -1;
853  dwErrCode = GetLastError();
854  }
855 
856  buf->hFileMappingObject = INVALID_HANDLE_VALUE;
857  buf->lpBaseAddress = NULL;
858  buf->dwNumberOfBytesToMap = 0;
859  }
860 
861  if (dwErrCode) {
862  if (!buf->dwErrCode)
863  buf->dwErrCode = dwErrCode;
864  SetLastError(dwErrCode);
865  }
866 #elif _POSIX_MAPPED_FILES >= 200112L
867  int errsv = 0;
868  if (buf->errsv) {
869  result = -1;
870  errsv = buf->errsv;
871  }
872 
873  if (buf->addr != MAP_FAILED) {
874  if (msync(buf->addr, buf->len, MS_SYNC) == -1 && !result) {
875  result = -1;
876  errsv = errno;
877  }
878  if (munmap(buf->addr, buf->len) == -1 && !result) {
879  result = -1;
880  errsv = errno;
881  }
882 
883  buf->addr = MAP_FAILED;
884  buf->len = 0;
885  }
886 
887  if (errsv) {
888  if (!buf->errsv)
889  buf->errsv = errsv;
890  errno = errsv;
891  }
892 #else
893  int errc = 0;
894  if (buf->errnum) {
895  result = -1;
896  errc = errnum2c(buf->errnum);
897  }
898 
899  if (buf->map) {
900  // Write the memory map to the file. We set the map pointer to
901  // NULL before writing to prevent an unnecessary update of the
902  // memory map by fwbuf_write().
903  void *map = buf->map;
904  buf->map = NULL;
905  if (fwbuf_pwrite(buf, map, buf->len, buf->pos)
906  != (ssize_t)buf->len
907  && !result) {
908  result = -1;
909  errc = get_errc();
910  }
911  free(map);
912 
913  buf->pos = 0;
914  buf->len = 0;
915  }
916 
917  if (errc) {
918  if (!buf->errnum)
919  buf->errnum = errc2num(errc);
920  set_errc(errc);
921  }
922 #endif
923 
924  return result;
925 }
926 
927 void
929 {
930  assert(buf);
931 
932 #if _WIN32
933  buf->dwErrCode = 0;
934 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
935  buf->errsv = 0;
936 #else
937  buf->errnum = 0;
938 #endif
939 }
940 
941 int
943 {
944  assert(buf);
945 
946 #if _WIN32
947  if (buf->dwErrCode)
948  SetLastError(buf->dwErrCode);
949  return !!buf->dwErrCode;
950 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
951  if (buf->errsv)
952  errno = buf->errsv;
953  return !!buf->errsv;
954 #else
955  if (buf->errnum)
956  set_errnum(buf->errnum);
957  return !!buf->errnum;
958 #endif
959 }
960 
961 void
963 {
964  assert(buf);
965 
966 #if _WIN32
967  if (!buf->dwErrCode)
968  buf->dwErrCode = ERROR_OPERATION_ABORTED;
969 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
970  if (!buf->errsv)
971  buf->errsv = ECANCELED;
972 #else
973  if (!buf->errnum)
974  buf->errnum = ERRNUM_CANCELED;
975 #endif
976 }
977 
978 int
980 {
981  fwbuf_unmap(buf);
982 
983  int result = fwbuf_error(buf) ? -1 : 0;
984 #if _WIN32
985  DWORD dwErrCode = GetLastError();
986 
987  if (buf->hFile == INVALID_HANDLE_VALUE)
988  goto done;
989 
990  // Only invoke FlushFileBuffers() if no error occurred.
991  if (!result && !FlushFileBuffers(buf->hFile)) {
992  result = -1;
993  dwErrCode = GetLastError();
994  }
995 
996  if (!CloseHandle(buf->hFile) && !result) {
997  result = -1;
998  dwErrCode = GetLastError();
999  }
1000  buf->hFile = INVALID_HANDLE_VALUE;
1001 
1002  // clang-format off
1003  if (result || !MoveFileExA(buf->tmpname, buf->filename,
1004  MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) {
1005  // clang-format on
1006  if (!result) {
1007  result = -1;
1008  dwErrCode = GetLastError();
1009  }
1010  DeleteFileA(buf->tmpname);
1011  }
1012 
1013 done:
1014  SetLastError(buf->dwErrCode = dwErrCode);
1015  return result;
1016 #elif _POSIX_C_SOURCE >= 200112L && !defined(__NEWLIB__)
1017  int errsv = errno;
1018 
1019  if (buf->fd == -1)
1020  goto done;
1021 
1022  // Only invoke fsync() if no error occurred.
1023  if (!result && fsync(buf->fd) == -1) {
1024  result = -1;
1025  errsv = errno;
1026  }
1027 
1028  if (close(buf->fd) == -1 && !result) {
1029  result = -1;
1030  errsv = errno;
1031  }
1032  buf->fd = -1;
1033 
1034  if (result || rename(buf->tmpname, buf->filename) == -1) {
1035  if (!result) {
1036  result = -1;
1037  errsv = errno;
1038  }
1039  remove(buf->tmpname);
1040  }
1041 
1042  if (!result && fsync(buf->dirfd) == -1) {
1043  result = -1;
1044  errsv = errno;
1045  }
1046 
1047  if (close(buf->dirfd) == -1 && !result) {
1048  result = -1;
1049  errsv = errno;
1050  }
1051  buf->dirfd = -1;
1052 
1053 done:
1054  errno = buf->errsv = errsv;
1055  return result;
1056 #else
1057  int errc = get_errc();
1058 
1059  if (!buf->stream)
1060  goto done;
1061 
1062  // Add zeros to the end of the file to reach the requested size.
1063  if (!result && buf->last < buf->size) {
1064  if (fwbuf_set_pos(buf, buf->last) != buf->last) {
1065  result = -1;
1066  errc = errno2c(errno);
1067  }
1068  while (!result && buf->last < buf->size) {
1069  if (fputc(0, buf->stream) == EOF) {
1070  result = -1;
1071  buf->errnum = errno2num(errno);
1072  errc = errno2c(errno);
1073  }
1074  buf->last++;
1075  }
1076  }
1077 
1078  // Only invoke fflush() if no error occurred.
1079  if (!result && fflush(buf->stream) == EOF) {
1080  result = -1;
1081  buf->errnum = errno2num(errno);
1082  errc = errno2c(errno);
1083  }
1084 
1085  if (fclose(buf->stream) == EOF && !result) {
1086  result = -1;
1087  buf->errnum = errno2num(errno);
1088  errc = errno2c(errno);
1089  }
1090  buf->stream = NULL;
1091 
1092  // WARNING: rename() may fail if the file already exists. Unfortunately,
1093  // if we remove the old file, we cannot guarantee that we won't lose
1094  // data.
1095  if (result || rename(buf->tmpname, buf->filename)) {
1096  if (!result) {
1097  result = -1;
1098  buf->errnum = errno2num(errno);
1099  errc = errno2c(errno);
1100  }
1101  remove(buf->tmpname);
1102  }
1103 
1104 done:
1105  set_errc(errc);
1106  return result;
1107 #endif
1108 }
1109 
1110 #endif // !LELY_NO_STDIO
This header file is part of the utilities library; it contains the native and platform-independent er...
errnum_t errno2num(int errnum)
Transforms a standard C error number to a platform-independent error number.
Definition: errnum.c:56
errnum_t errc2num(int errc)
Transforms a native error code to a platform-independent error number.
Definition: errnum.c:309
int errnum2c(errnum_t errnum)
Transforms a platform-independent error number to a native error code.
Definition: errnum.c:810
errnum
The platform-independent error numbers.
Definition: errnum.h:77
@ ERRNUM_INVAL
Invalid argument.
Definition: errnum.h:132
@ ERRNUM_OVERFLOW
Value too large to be stored in data type.
Definition: errnum.h:204
@ ERRNUM_CANCELED
Operation canceled.
Definition: errnum.h:99
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
int errno2c(int errnum)
Transforms a standard C error number to a native error code.
Definition: errnum.c:46
void set_errnum(errnum_t errnum)
Sets the current (thread-specific) platform-independent error number to errnum.
Definition: errnum.h:424
int fwbuf_error(fwbuf_t *buf)
Returns 1 if the error indicator of a write file buffer is set, and 0 if not.
Definition: fwbuf.c:942
intmax_t fwbuf_set_pos(fwbuf_t *buf, intmax_t pos)
Sets the current offset (in bytes) of a write file buffer with respect to the beginning of the file.
Definition: fwbuf.c:447
int fwbuf_set_size(fwbuf_t *buf, intmax_t size)
Sets the new size (in bytes) of the a write file buffer.
Definition: fwbuf.c:369
ssize_t fwbuf_write(fwbuf_t *buf, const void *ptr, size_t size)
Writes bytes to the current position in a write file buffer.
Definition: fwbuf.c:489
int fwbuf_unmap(fwbuf_t *buf)
Unmaps the current memory map of a write file buffer, if it exists, and writes the changes to disk.
Definition: fwbuf.c:827
void * fwbuf_map(fwbuf_t *buf, intmax_t pos, size_t *psize)
Maps (part of) the contents of a write file buffer to memory.
Definition: fwbuf.c:658
void fwbuf_destroy(fwbuf_t *buf)
Destroys a write file buffer.
Definition: fwbuf.c:330
int fwbuf_commit(fwbuf_t *buf)
Commits all changes to a write file buffer to disk if all previous file operations were successful,...
Definition: fwbuf.c:979
fwbuf_t * fwbuf_create(const char *filename)
Creates a new (atomic) write file buffer.
Definition: fwbuf.c:305
intmax_t fwbuf_get_pos(fwbuf_t *buf)
Returns the current offset (in bytes) of a write file buffer with respect to the beginning of the fil...
Definition: fwbuf.c:415
ssize_t fwbuf_pwrite(fwbuf_t *buf, const void *ptr, size_t size, intmax_t pos)
Writes bytes to the specified position in a write file buffer.
Definition: fwbuf.c:543
void fwbuf_cancel(fwbuf_t *buf)
Cancels any further file operations by setting the error indicator of a write file buffer to ERRNUM_C...
Definition: fwbuf.c:962
intmax_t fwbuf_get_size(fwbuf_t *buf)
Returns the current size (in bytes) of the a write file buffer, or -1 on error.
Definition: fwbuf.c:339
void fwbuf_clearerr(fwbuf_t *buf)
Clears the error indicator of a write file buffer, allowing fwbuf_commit() to write the file to disk.
Definition: fwbuf.c:928
This header file is part of the utilities library; it contains the (atomic) write file buffer declara...
#define MIN(a, b)
Returns the minimum of a and b.
Definition: util.h:57
#define MAX(a, b)
Returns the maximum of a and b.
Definition: util.h:65
This is the internal header file of the utilities library.
This header file is part of the C11 and POSIX compatibility library; it includes <stdio....
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....
This header file is part of the C11 and POSIX compatibility library; it includes <string....
An (atomic) write file buffer struct.
Definition: fwbuf.c:59
int fd
The file descriptor.
Definition: fwbuf.c:81
void * addr
The base address of the current file mapping.
Definition: fwbuf.c:85
int errsv
The number of the first error that occurred during a file operation.
Definition: fwbuf.c:83
char * filename
A pointer to the name of the file.
Definition: fwbuf.c:61
int dirfd
The file descriptor of the directory containing the temporary file.
Definition: fwbuf.c:79
size_t len
The length (in bytes) of the mapping at addr.
Definition: fwbuf.c:87
char * tmpname
A pointer to the name of the temporary file.
Definition: fwbuf.c:77