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
56struct __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
108void *
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
117void
118__fwbuf_free(void *ptr)
119{
120 free(ptr);
121}
122
123struct __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
168error_CreateFileA:
169 DeleteFileA(buf->tmpname);
170error_GetTempFileNameA:
171 free(buf->filename);
172error_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
238error_fcntl:
239#endif
240error_open_fd:
241 free(buf->tmpname);
242error_malloc_tmpname:
243 close(buf->dirfd);
244error_open_dirfd:
245 if (tmp)
246 free(tmp);
247error_strdup_tmp:
248 free(buf->filename);
249error_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
278error_fopen:
279 free(buf->filename);
280error_strdup:
281 set_errc(errc);
282 return NULL;
283#endif
284}
285
286void
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
300fwbuf_t *
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
318error_init_buf:
319 __fwbuf_free(buf);
320error_alloc_buf:
321 set_errc(errc);
322 return NULL;
323}
324
325void
327{
328 if (buf) {
329 __fwbuf_fini(buf);
330 __fwbuf_free(buf);
331 }
332}
333
334intmax_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
364int
365fwbuf_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
410intmax_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
442intmax_t
443fwbuf_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
485fwbuf_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
539fwbuf_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
580error_WriteFile:
581 fwbuf_set_pos(buf, oldpos);
582error_get_pos:
583error_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
642error_fputc:
643error_set_pos:
644 if (fwbuf_set_pos(buf, oldpos) == -1 && !errc)
645 errc = get_errc();
646error_get_pos:
647error_pos:
648 set_errc(errc);
649 return result;
650#endif
651}
652
653void *
654fwbuf_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
719error_MapViewOfFile:
720 CloseHandle(buf->hFileMappingObject);
721 buf->hFileMappingObject = INVALID_HANDLE_VALUE;
722error_CreateFileMapping:
723error_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
808error_fread:
809error_set_pos:
810 if (fwbuf_set_pos(buf, oldpos) == -1 && !errc)
811 errc = get_errc();
812error_get_pos:
813 free(buf->map);
814 buf->map = NULL;
815error_malloc_map:
816 set_errc(errc);
817 return NULL;
818#endif
819}
820
821int
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
922void
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
936int
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
956void
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
973int
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
1008done:
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
1048done:
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
1099done:
1100 set_errc(errc);
1101 return result;
1102#endif
1103}
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:937
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
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
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
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
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
void fwbuf_destroy(fwbuf_t *buf)
Destroys a write file buffer.
Definition: fwbuf.c:326
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
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
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
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
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
fwbuf_t * fwbuf_create(const char *filename)
Creates a new (atomic) write file buffer.
Definition: fwbuf.c:301
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
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
ptrdiff_t ssize_t
Used for a count of bytes or an error indication.
Definition: types.h:43