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
59struct __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
111void *
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
120void
121__fwbuf_free(void *ptr)
122{
123 free(ptr);
124}
125
126struct __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
171error_CreateFileA:
172 DeleteFileA(buf->tmpname);
173error_GetTempFileNameA:
174 free(buf->filename);
175error_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
241error_fcntl:
242#endif
243error_open_fd:
244 free(buf->tmpname);
245error_malloc_tmpname:
246 close(buf->dirfd);
247error_open_dirfd:
248 if (tmp)
249 free(tmp);
250error_strdup_tmp:
251 free(buf->filename);
252error_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
282error_fopen:
283 free(buf->filename);
284error_strdup:
285 set_errc(errc);
286 return NULL;
287#endif
288}
289
290void
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
304fwbuf_t *
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
322error_init_buf:
323 __fwbuf_free(buf);
324error_alloc_buf:
325 set_errc(errc);
326 return NULL;
327}
328
329void
331{
332 if (buf) {
333 __fwbuf_fini(buf);
334 __fwbuf_free(buf);
335 }
336}
337
338intmax_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
368int
369fwbuf_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
414intmax_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
446intmax_t
447fwbuf_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
488ssize_t
489fwbuf_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
542ssize_t
543fwbuf_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
584error_WriteFile:
585 fwbuf_set_pos(buf, oldpos);
586error_get_pos:
587error_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
646error_fputc:
647error_set_pos:
648 if (fwbuf_set_pos(buf, oldpos) == -1 && !errc)
649 errc = get_errc();
650error_get_pos:
651error_pos:
652 set_errc(errc);
653 return result;
654#endif
655}
656
657void *
658fwbuf_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
724error_MapViewOfFile:
725 CloseHandle(buf->hFileMappingObject);
726 buf->hFileMappingObject = INVALID_HANDLE_VALUE;
727error_CreateFileMapping:
728error_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
813error_fread:
814error_set_pos:
815 if (fwbuf_set_pos(buf, oldpos) == -1 && !errc)
816 errc = get_errc();
817error_get_pos:
818 free(buf->map);
819 buf->map = NULL;
820error_malloc_map:
821 set_errc(errc);
822 return NULL;
823#endif
824}
825
826int
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
927void
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
941int
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
961void
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
978int
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
1013done:
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
1053done:
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
1104done:
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
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
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_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
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
fwbuf_t * fwbuf_create(const char *filename)
Creates a new (atomic) write file buffer.
Definition: fwbuf.c:305
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