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