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