1 /*
2  * This file Copyright (C) 2013-2017 Mnemosyne LLC
3  *
4  * It may be used under the GNU GPL versions 2 or 3
5  * or any future license endorsed by Mnemosyne LLC.
6  *
7  */
8 
9 #undef _GNU_SOURCE
10 #define _GNU_SOURCE
11 
12 #include <dirent.h>
13 #include <errno.h>
14 #include <fcntl.h> /* O_LARGEFILE, posix_fadvise(), [posix_]fallocate(), fcntl() */
15 #include <libgen.h> /* basename(), dirname() */
16 #include <limits.h> /* PATH_MAX */
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/types.h>
21 #include <sys/file.h> /* flock() */
22 #include <sys/mman.h> /* mmap(), munmap() */
23 #include <sys/stat.h>
24 #include <unistd.h> /* lseek(), write(), ftruncate(), pread(), pwrite(), pathconf(), etc */
25 
26 #ifdef HAVE_XFS_XFS_H
27 #include <xfs/xfs.h>
28 #endif
29 
30 #include "transmission.h"
31 #include "error.h"
32 #include "file.h"
33 #include "log.h"
34 #include "platform.h"
35 #include "tr-assert.h"
36 #include "utils.h"
37 
38 #ifndef O_LARGEFILE
39 #define O_LARGEFILE 0
40 #endif
41 #ifndef O_BINARY
42 #define O_BINARY 0
43 #endif
44 #ifndef O_SEQUENTIAL
45 #define O_SEQUENTIAL 0
46 #endif
47 #ifndef O_CLOEXEC
48 #define O_CLOEXEC 0
49 #endif
50 
51 #ifndef PATH_MAX
52 #define PATH_MAX 4096
53 #endif
54 
55 /* don't use pread/pwrite on old versions of uClibc because they're buggy.
56  * https://trac.transmissionbt.com/ticket/3826 */
57 #if defined(__UCLIBC__) && !TR_UCLIBC_CHECK_VERSION(0, 9, 28)
58 #undef HAVE_PREAD
59 #undef HAVE_PWRITE
60 #endif
61 
62 #ifdef __APPLE__
63 #ifndef HAVE_PREAD
64 #define HAVE_PREAD
65 #endif
66 #ifndef HAVE_PWRITE
67 #define HAVE_PWRITE
68 #endif
69 #ifndef HAVE_MKDTEMP
70 #define HAVE_MKDTEMP
71 #endif
72 #endif
73 
set_system_error(tr_error ** error,int code)74 static void set_system_error(tr_error** error, int code)
75 {
76     if (error == NULL)
77     {
78         return;
79     }
80 
81     tr_error_set_literal(error, code, tr_strerror(code));
82 }
83 
set_system_error_if_file_found(tr_error ** error,int code)84 static void set_system_error_if_file_found(tr_error** error, int code)
85 {
86     if (code != ENOENT)
87     {
88         set_system_error(error, code);
89     }
90 }
91 
stat_to_sys_path_info(struct stat const * sb,tr_sys_path_info * info)92 static void stat_to_sys_path_info(struct stat const* sb, tr_sys_path_info* info)
93 {
94     if (S_ISREG(sb->st_mode))
95     {
96         info->type = TR_SYS_PATH_IS_FILE;
97     }
98     else if (S_ISDIR(sb->st_mode))
99     {
100         info->type = TR_SYS_PATH_IS_DIRECTORY;
101     }
102     else
103     {
104         info->type = TR_SYS_PATH_IS_OTHER;
105     }
106 
107     info->size = (uint64_t)sb->st_size;
108     info->last_modified_at = sb->st_mtime;
109 }
110 
set_file_for_single_pass(tr_sys_file_t handle)111 static void set_file_for_single_pass(tr_sys_file_t handle)
112 {
113     /* Set hints about the lookahead buffer and caching. It's okay
114        for these to fail silently, so don't let them affect errno */
115 
116     int const err = errno;
117 
118     if (handle == TR_BAD_SYS_FILE)
119     {
120         return;
121     }
122 
123 #ifdef HAVE_POSIX_FADVISE
124 
125     (void)posix_fadvise(handle, 0, 0, POSIX_FADV_SEQUENTIAL);
126 
127 #endif
128 
129 #ifdef __APPLE__
130 
131     (void)fcntl(handle, F_RDAHEAD, 1);
132     (void)fcntl(handle, F_NOCACHE, 1);
133 
134 #endif
135 
136     errno = err;
137 }
138 
139 #ifndef HAVE_MKDIRP
140 
create_path_require_dir(char const * path,tr_error ** error)141 static bool create_path_require_dir(char const* path, tr_error** error)
142 {
143     struct stat sb;
144 
145     if (stat(path, &sb) == -1)
146     {
147         set_system_error(error, errno);
148         return false;
149     }
150 
151     if ((sb.st_mode & S_IFMT) != S_IFDIR)
152     {
153         tr_error_set(error, ENOTDIR, _("File \"%s\" is in the way"), path);
154         return false;
155     }
156 
157     return true;
158 }
159 
create_path(char const * path_in,int permissions,tr_error ** error)160 static bool create_path(char const* path_in, int permissions, tr_error** error)
161 {
162     /* make a temporary copy of path */
163     char* path = tr_strdup(path_in);
164 
165     /* walk past the root */
166     char* p = path;
167 
168     while (*p == TR_PATH_DELIMITER)
169     {
170         ++p;
171     }
172 
173     char* path_end = p + strlen(p);
174 
175     while (path_end > path && *path_end == TR_PATH_DELIMITER)
176     {
177         --path_end;
178     }
179 
180     char* pp;
181     bool ret = false;
182     tr_error* my_error = NULL;
183 
184     /* Go one level up on each iteration and attempt to create */
185     for (pp = path_end; pp != NULL; pp = strrchr(p, TR_PATH_DELIMITER))
186     {
187         *pp = '\0';
188 
189         ret = mkdir(path, permissions) != -1;
190 
191         if (ret)
192         {
193             break;
194         }
195 
196         if (errno == EEXIST)
197         {
198             ret = create_path_require_dir(path, &my_error);
199 
200             if (ret)
201             {
202                 break;
203             }
204 
205             goto failure;
206         }
207 
208         if (errno != ENOENT)
209         {
210             set_system_error(&my_error, errno);
211             goto failure;
212         }
213     }
214 
215     if (ret && pp == path_end)
216     {
217         goto cleanup;
218     }
219 
220     /* Go one level down on each iteration and attempt to create */
221     for (pp = pp == NULL ? p + strlen(p) : pp; pp < path_end; pp += strlen(pp))
222     {
223         *pp = TR_PATH_DELIMITER;
224 
225         if (mkdir(path, permissions) == -1)
226         {
227             break;
228         }
229     }
230 
231     ret = create_path_require_dir(path, &my_error);
232 
233     if (ret)
234     {
235         goto cleanup;
236     }
237 
238 failure:
239 
240     TR_ASSERT(!ret);
241     TR_ASSERT(my_error != NULL);
242 
243     tr_logAddError(_("Couldn't create \"%1$s\": %2$s"), path, my_error->message);
244     tr_error_propagate(error, &my_error);
245 
246 cleanup:
247 
248     TR_ASSERT(my_error == NULL);
249 
250     tr_free(path);
251     return ret;
252 }
253 
254 #endif
255 
tr_sys_path_exists(char const * path,tr_error ** error)256 bool tr_sys_path_exists(char const* path, tr_error** error)
257 {
258     TR_ASSERT(path != NULL);
259 
260     bool ret = access(path, F_OK) != -1;
261 
262     if (!ret)
263     {
264         set_system_error_if_file_found(error, errno);
265     }
266 
267     return ret;
268 }
269 
tr_sys_path_get_info(char const * path,int flags,tr_sys_path_info * info,tr_error ** error)270 bool tr_sys_path_get_info(char const* path, int flags, tr_sys_path_info* info, tr_error** error)
271 {
272     TR_ASSERT(path != NULL);
273     TR_ASSERT(info != NULL);
274 
275     bool ret;
276     struct stat sb;
277 
278     if ((flags & TR_SYS_PATH_NO_FOLLOW) == 0)
279     {
280         ret = stat(path, &sb) != -1;
281     }
282     else
283     {
284         ret = lstat(path, &sb) != -1;
285     }
286 
287     if (ret)
288     {
289         stat_to_sys_path_info(&sb, info);
290     }
291     else
292     {
293         set_system_error(error, errno);
294     }
295 
296     return ret;
297 }
298 
tr_sys_path_is_relative(char const * path)299 bool tr_sys_path_is_relative(char const* path)
300 {
301     TR_ASSERT(path != NULL);
302 
303     return path[0] != '/';
304 }
305 
tr_sys_path_is_same(char const * path1,char const * path2,tr_error ** error)306 bool tr_sys_path_is_same(char const* path1, char const* path2, tr_error** error)
307 {
308     TR_ASSERT(path1 != NULL);
309     TR_ASSERT(path2 != NULL);
310 
311     bool ret = false;
312     struct stat sb1;
313     struct stat sb2;
314 
315     if (stat(path1, &sb1) != -1 && stat(path2, &sb2) != -1)
316     {
317         ret = sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino;
318     }
319     else
320     {
321         set_system_error_if_file_found(error, errno);
322     }
323 
324     return ret;
325 }
326 
tr_sys_path_resolve(char const * path,tr_error ** error)327 char* tr_sys_path_resolve(char const* path, tr_error** error)
328 {
329     TR_ASSERT(path != NULL);
330 
331     char* ret = NULL;
332     char* tmp = NULL;
333 
334 #if defined(HAVE_CANONICALIZE_FILE_NAME)
335 
336     ret = canonicalize_file_name(path);
337 
338 #endif
339 
340 #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L
341 
342     /* Better safe than sorry: realpath() officially supports NULL as destination
343        starting off POSIX.1-2008. */
344 
345     if (ret == NULL)
346     {
347         ret = realpath(path, NULL);
348     }
349 
350 #endif
351 
352     if (ret == NULL)
353     {
354         tmp = tr_new(char, PATH_MAX);
355         ret = realpath(path, tmp);
356 
357         if (ret != NULL)
358         {
359             ret = tr_strdup(ret);
360         }
361     }
362 
363     if (ret == NULL)
364     {
365         set_system_error(error, errno);
366     }
367 
368     tr_free(tmp);
369 
370     return ret;
371 }
372 
tr_sys_path_basename(char const * path,tr_error ** error)373 char* tr_sys_path_basename(char const* path, tr_error** error)
374 {
375     TR_ASSERT(path != NULL);
376 
377     char* ret = NULL;
378     char* tmp;
379 
380     tmp = tr_strdup(path);
381     ret = basename(tmp);
382 
383     if (ret != NULL)
384     {
385         ret = tr_strdup(ret);
386     }
387     else
388     {
389         set_system_error(error, errno);
390     }
391 
392     tr_free(tmp);
393 
394     return ret;
395 }
396 
tr_sys_path_dirname(char const * path,tr_error ** error)397 char* tr_sys_path_dirname(char const* path, tr_error** error)
398 {
399     TR_ASSERT(path != NULL);
400 
401     char* tmp = tr_strdup(path);
402     char* ret = dirname(tmp);
403 
404     if (ret != NULL)
405     {
406         ret = tr_strdup(ret);
407     }
408     else
409     {
410         set_system_error(error, errno);
411     }
412 
413     tr_free(tmp);
414 
415     return ret;
416 }
417 
tr_sys_path_rename(char const * src_path,char const * dst_path,tr_error ** error)418 bool tr_sys_path_rename(char const* src_path, char const* dst_path, tr_error** error)
419 {
420     TR_ASSERT(src_path != NULL);
421     TR_ASSERT(dst_path != NULL);
422 
423     bool ret = rename(src_path, dst_path) != -1;
424 
425     if (!ret)
426     {
427         set_system_error(error, errno);
428     }
429 
430     return ret;
431 }
432 
tr_sys_path_remove(char const * path,tr_error ** error)433 bool tr_sys_path_remove(char const* path, tr_error** error)
434 {
435     TR_ASSERT(path != NULL);
436 
437     bool ret = remove(path) != -1;
438 
439     if (!ret)
440     {
441         set_system_error(error, errno);
442     }
443 
444     return ret;
445 }
446 
tr_sys_path_native_separators(char * path)447 char* tr_sys_path_native_separators(char* path)
448 {
449     return path;
450 }
451 
tr_sys_file_get_std(tr_std_sys_file_t std_file,tr_error ** error)452 tr_sys_file_t tr_sys_file_get_std(tr_std_sys_file_t std_file, tr_error** error)
453 {
454     tr_sys_file_t ret = TR_BAD_SYS_FILE;
455 
456     switch (std_file)
457     {
458     case TR_STD_SYS_FILE_IN:
459         ret = STDIN_FILENO;
460         break;
461 
462     case TR_STD_SYS_FILE_OUT:
463         ret = STDOUT_FILENO;
464         break;
465 
466     case TR_STD_SYS_FILE_ERR:
467         ret = STDERR_FILENO;
468         break;
469 
470     default:
471         TR_ASSERT_MSG(false, "unknown standard file %d", (int)std_file);
472         set_system_error(error, EINVAL);
473     }
474 
475     return ret;
476 }
477 
tr_sys_file_open(char const * path,int flags,int permissions,tr_error ** error)478 tr_sys_file_t tr_sys_file_open(char const* path, int flags, int permissions, tr_error** error)
479 {
480     TR_ASSERT(path != NULL);
481     TR_ASSERT((flags & (TR_SYS_FILE_READ | TR_SYS_FILE_WRITE)) != 0);
482 
483     tr_sys_file_t ret;
484     int native_flags = 0;
485 
486     if ((flags & (TR_SYS_FILE_READ | TR_SYS_FILE_WRITE)) == (TR_SYS_FILE_READ | TR_SYS_FILE_WRITE))
487     {
488         native_flags |= O_RDWR;
489     }
490     else if ((flags & TR_SYS_FILE_READ) != 0)
491     {
492         native_flags |= O_RDONLY;
493     }
494     else if ((flags & TR_SYS_FILE_WRITE) != 0)
495     {
496         native_flags |= O_WRONLY;
497     }
498 
499     native_flags |=
500         ((flags & TR_SYS_FILE_CREATE) != 0 ? O_CREAT : 0) |
501         ((flags & TR_SYS_FILE_CREATE_NEW) != 0 ? O_CREAT | O_EXCL : 0) |
502         ((flags & TR_SYS_FILE_APPEND) != 0 ? O_APPEND : 0) |
503         ((flags & TR_SYS_FILE_TRUNCATE) != 0 ? O_TRUNC : 0) |
504         ((flags & TR_SYS_FILE_SEQUENTIAL) != 0 ? O_SEQUENTIAL : 0) |
505         O_BINARY | O_LARGEFILE | O_CLOEXEC;
506 
507     ret = open(path, native_flags, permissions);
508 
509     if (ret != TR_BAD_SYS_FILE)
510     {
511         if ((flags & TR_SYS_FILE_SEQUENTIAL) != 0)
512         {
513             set_file_for_single_pass(ret);
514         }
515     }
516     else
517     {
518         set_system_error(error, errno);
519     }
520 
521     return ret;
522 }
523 
tr_sys_file_open_temp(char * path_template,tr_error ** error)524 tr_sys_file_t tr_sys_file_open_temp(char* path_template, tr_error** error)
525 {
526     TR_ASSERT(path_template != NULL);
527 
528     tr_sys_file_t ret = mkstemp(path_template);
529 
530     if (ret == TR_BAD_SYS_FILE)
531     {
532         set_system_error(error, errno);
533     }
534 
535     set_file_for_single_pass(ret);
536 
537     return ret;
538 }
539 
tr_sys_file_close(tr_sys_file_t handle,tr_error ** error)540 bool tr_sys_file_close(tr_sys_file_t handle, tr_error** error)
541 {
542     TR_ASSERT(handle != TR_BAD_SYS_FILE);
543 
544     bool ret = close(handle) != -1;
545 
546     if (!ret)
547     {
548         set_system_error(error, errno);
549     }
550 
551     return ret;
552 }
553 
tr_sys_file_get_info(tr_sys_file_t handle,tr_sys_path_info * info,tr_error ** error)554 bool tr_sys_file_get_info(tr_sys_file_t handle, tr_sys_path_info* info, tr_error** error)
555 {
556     TR_ASSERT(handle != TR_BAD_SYS_FILE);
557     TR_ASSERT(info != NULL);
558 
559     struct stat sb;
560     bool ret = fstat(handle, &sb) != -1;
561 
562     if (ret)
563     {
564         stat_to_sys_path_info(&sb, info);
565     }
566     else
567     {
568         set_system_error(error, errno);
569     }
570 
571     return ret;
572 }
573 
tr_sys_file_seek(tr_sys_file_t handle,int64_t offset,tr_seek_origin_t origin,uint64_t * new_offset,tr_error ** error)574 bool tr_sys_file_seek(tr_sys_file_t handle, int64_t offset, tr_seek_origin_t origin, uint64_t* new_offset, tr_error** error)
575 {
576     TR_STATIC_ASSERT(TR_SEEK_SET == SEEK_SET, "values should match");
577     TR_STATIC_ASSERT(TR_SEEK_CUR == SEEK_CUR, "values should match");
578     TR_STATIC_ASSERT(TR_SEEK_END == SEEK_END, "values should match");
579 
580     TR_ASSERT(handle != TR_BAD_SYS_FILE);
581     TR_ASSERT(origin == TR_SEEK_SET || origin == TR_SEEK_CUR || origin == TR_SEEK_END);
582 
583     bool ret = false;
584     off_t my_new_offset;
585 
586     TR_STATIC_ASSERT(sizeof(*new_offset) >= sizeof(my_new_offset), "");
587 
588     my_new_offset = lseek(handle, offset, origin);
589 
590     if (my_new_offset != -1)
591     {
592         if (new_offset != NULL)
593         {
594             *new_offset = my_new_offset;
595         }
596 
597         ret = true;
598     }
599     else
600     {
601         set_system_error(error, errno);
602     }
603 
604     return ret;
605 }
606 
tr_sys_file_read(tr_sys_file_t handle,void * buffer,uint64_t size,uint64_t * bytes_read,tr_error ** error)607 bool tr_sys_file_read(tr_sys_file_t handle, void* buffer, uint64_t size, uint64_t* bytes_read, tr_error** error)
608 {
609     TR_ASSERT(handle != TR_BAD_SYS_FILE);
610     TR_ASSERT(buffer != NULL || size == 0);
611 
612     bool ret = false;
613     ssize_t my_bytes_read;
614 
615     TR_STATIC_ASSERT(sizeof(*bytes_read) >= sizeof(my_bytes_read), "");
616 
617     my_bytes_read = read(handle, buffer, size);
618 
619     if (my_bytes_read != -1)
620     {
621         if (bytes_read != NULL)
622         {
623             *bytes_read = my_bytes_read;
624         }
625 
626         ret = true;
627     }
628     else
629     {
630         set_system_error(error, errno);
631     }
632 
633     return ret;
634 }
635 
tr_sys_file_read_at(tr_sys_file_t handle,void * buffer,uint64_t size,uint64_t offset,uint64_t * bytes_read,tr_error ** error)636 bool tr_sys_file_read_at(tr_sys_file_t handle, void* buffer, uint64_t size, uint64_t offset, uint64_t* bytes_read,
637     tr_error** error)
638 {
639     TR_ASSERT(handle != TR_BAD_SYS_FILE);
640     TR_ASSERT(buffer != NULL || size == 0);
641     /* seek requires signed offset, so it should be in mod range */
642     TR_ASSERT(offset < UINT64_MAX / 2);
643 
644     bool ret = false;
645     ssize_t my_bytes_read;
646 
647     TR_STATIC_ASSERT(sizeof(*bytes_read) >= sizeof(my_bytes_read), "");
648 
649 #ifdef HAVE_PREAD
650 
651     my_bytes_read = pread(handle, buffer, size, offset);
652 
653 #else
654 
655     if (lseek(handle, offset, SEEK_SET) != -1)
656     {
657         my_bytes_read = read(handle, buffer, size);
658     }
659     else
660     {
661         my_bytes_read = -1;
662     }
663 
664 #endif
665 
666     if (my_bytes_read != -1)
667     {
668         if (bytes_read != NULL)
669         {
670             *bytes_read = my_bytes_read;
671         }
672 
673         ret = true;
674     }
675     else
676     {
677         set_system_error(error, errno);
678     }
679 
680     return ret;
681 }
682 
tr_sys_file_write(tr_sys_file_t handle,void const * buffer,uint64_t size,uint64_t * bytes_written,tr_error ** error)683 bool tr_sys_file_write(tr_sys_file_t handle, void const* buffer, uint64_t size, uint64_t* bytes_written, tr_error** error)
684 {
685     TR_ASSERT(handle != TR_BAD_SYS_FILE);
686     TR_ASSERT(buffer != NULL || size == 0);
687 
688     bool ret = false;
689     ssize_t my_bytes_written;
690 
691     TR_STATIC_ASSERT(sizeof(*bytes_written) >= sizeof(my_bytes_written), "");
692 
693     my_bytes_written = write(handle, buffer, size);
694 
695     if (my_bytes_written != -1)
696     {
697         if (bytes_written != NULL)
698         {
699             *bytes_written = my_bytes_written;
700         }
701 
702         ret = true;
703     }
704     else
705     {
706         set_system_error(error, errno);
707     }
708 
709     return ret;
710 }
711 
tr_sys_file_write_at(tr_sys_file_t handle,void const * buffer,uint64_t size,uint64_t offset,uint64_t * bytes_written,tr_error ** error)712 bool tr_sys_file_write_at(tr_sys_file_t handle, void const* buffer, uint64_t size, uint64_t offset, uint64_t* bytes_written,
713     tr_error** error)
714 {
715     TR_ASSERT(handle != TR_BAD_SYS_FILE);
716     TR_ASSERT(buffer != NULL || size == 0);
717     /* seek requires signed offset, so it should be in mod range */
718     TR_ASSERT(offset < UINT64_MAX / 2);
719 
720     bool ret = false;
721     ssize_t my_bytes_written;
722 
723     TR_STATIC_ASSERT(sizeof(*bytes_written) >= sizeof(my_bytes_written), "");
724 
725 #ifdef HAVE_PWRITE
726 
727     my_bytes_written = pwrite(handle, buffer, size, offset);
728 
729 #else
730 
731     if (lseek(handle, offset, SEEK_SET) != -1)
732     {
733         my_bytes_written = write(handle, buffer, size);
734     }
735     else
736     {
737         my_bytes_written = -1;
738     }
739 
740 #endif
741 
742     if (my_bytes_written != -1)
743     {
744         if (bytes_written != NULL)
745         {
746             *bytes_written = my_bytes_written;
747         }
748 
749         ret = true;
750     }
751     else
752     {
753         set_system_error(error, errno);
754     }
755 
756     return ret;
757 }
758 
tr_sys_file_flush(tr_sys_file_t handle,tr_error ** error)759 bool tr_sys_file_flush(tr_sys_file_t handle, tr_error** error)
760 {
761     TR_ASSERT(handle != TR_BAD_SYS_FILE);
762 
763     bool ret = fsync(handle) != -1;
764 
765     if (!ret)
766     {
767         set_system_error(error, errno);
768     }
769 
770     return ret;
771 }
772 
tr_sys_file_truncate(tr_sys_file_t handle,uint64_t size,tr_error ** error)773 bool tr_sys_file_truncate(tr_sys_file_t handle, uint64_t size, tr_error** error)
774 {
775     TR_ASSERT(handle != TR_BAD_SYS_FILE);
776 
777     bool ret = ftruncate(handle, size) != -1;
778 
779     if (!ret)
780     {
781         set_system_error(error, errno);
782     }
783 
784     return ret;
785 }
786 
tr_sys_file_advise(tr_sys_file_t handle,uint64_t offset,uint64_t size,tr_sys_file_advice_t advice,tr_error ** error)787 bool tr_sys_file_advise(tr_sys_file_t handle, uint64_t offset, uint64_t size, tr_sys_file_advice_t advice, tr_error** error)
788 {
789     TR_ASSERT(handle != TR_BAD_SYS_FILE);
790     TR_ASSERT(size > 0);
791     TR_ASSERT(advice == TR_SYS_FILE_ADVICE_WILL_NEED || advice == TR_SYS_FILE_ADVICE_DONT_NEED);
792 
793     bool ret = true;
794 
795 #if defined(HAVE_POSIX_FADVISE)
796 
797     int const native_advice = advice == TR_SYS_FILE_ADVICE_WILL_NEED ? POSIX_FADV_WILLNEED :
798         (advice == TR_SYS_FILE_ADVICE_DONT_NEED ? POSIX_FADV_DONTNEED : POSIX_FADV_NORMAL);
799 
800     TR_ASSERT(native_advice != POSIX_FADV_NORMAL);
801 
802     int const code = posix_fadvise(handle, offset, size, native_advice);
803 
804     if (code != 0)
805     {
806         set_system_error(error, code);
807         ret = false;
808     }
809 
810 #elif defined(__APPLE__)
811 
812     if (advice != TR_SYS_FILE_ADVICE_WILL_NEED)
813     {
814         goto skip_darwin_fcntl;
815     }
816 
817     struct radvisory const radv =
818     {
819         .ra_offset = offset,
820         .ra_count = size
821     };
822 
823     ret = fcntl(handle, F_RDADVISE, &radv) != -1;
824 
825     if (!ret)
826     {
827         set_system_error(error, errno);
828     }
829 
830 skip_darwin_fcntl:
831 
832 #else
833 
834     (void)handle;
835     (void)offset;
836     (void)size;
837     (void)advice;
838     (void)error;
839 
840 #endif
841 
842     return ret;
843 }
844 
tr_sys_file_preallocate(tr_sys_file_t handle,uint64_t size,int flags,tr_error ** error)845 bool tr_sys_file_preallocate(tr_sys_file_t handle, uint64_t size, int flags, tr_error** error)
846 {
847     TR_ASSERT(handle != TR_BAD_SYS_FILE);
848 
849     bool ret = false;
850 
851     errno = 0;
852 
853 #ifdef HAVE_FALLOCATE64
854 
855     /* fallocate64 is always preferred, so try it first */
856     ret = fallocate64(handle, 0, 0, size) != -1;
857 
858     if (ret || errno == ENOSPC)
859     {
860         goto out;
861     }
862 
863 #endif
864 
865     if ((flags & TR_SYS_FILE_PREALLOC_SPARSE) == 0)
866     {
867         int code = errno;
868 
869 #ifdef HAVE_XFS_XFS_H
870 
871         if (platform_test_xfs_fd(handle))
872         {
873             xfs_flock64_t fl;
874 
875             fl.l_whence = 0;
876             fl.l_start = 0;
877             fl.l_len = size;
878 
879             ret = xfsctl(NULL, handle, XFS_IOC_RESVSP64, &fl) != -1;
880 
881             if (ret)
882             {
883                 ret = ftruncate(handle, size) != -1;
884             }
885 
886             code = errno;
887 
888             if (ret || code == ENOSPC)
889             {
890                 goto non_sparse_out;
891             }
892         }
893 
894 #endif
895 
896 #ifdef __APPLE__
897 
898         {
899             fstore_t fst;
900 
901             fst.fst_flags = F_ALLOCATEALL;
902             fst.fst_posmode = F_PEOFPOSMODE;
903             fst.fst_offset = 0;
904             fst.fst_length = size;
905             fst.fst_bytesalloc = 0;
906 
907             ret = fcntl(handle, F_PREALLOCATE, &fst) != -1;
908 
909             if (ret)
910             {
911                 ret = ftruncate(handle, size) != -1;
912             }
913 
914             code = errno;
915 
916             if (ret || code == ENOSPC)
917             {
918                 goto non_sparse_out;
919             }
920         }
921 
922 #endif
923 
924 #ifdef HAVE_POSIX_FALLOCATE
925 
926         code = posix_fallocate(handle, 0, size);
927         ret = code == 0;
928 
929 #endif
930 
931 #if defined(HAVE_XFS_XFS_H) || defined(__APPLE__)
932 non_sparse_out:
933 #endif
934         errno = code;
935     }
936 
937 #ifdef HAVE_FALLOCATE64
938 out:
939 #endif
940 
941     if (!ret)
942     {
943         set_system_error(error, errno);
944     }
945 
946     return ret;
947 }
948 
tr_sys_file_map_for_reading(tr_sys_file_t handle,uint64_t offset,uint64_t size,tr_error ** error)949 void* tr_sys_file_map_for_reading(tr_sys_file_t handle, uint64_t offset, uint64_t size, tr_error** error)
950 {
951     TR_ASSERT(handle != TR_BAD_SYS_FILE);
952     TR_ASSERT(size > 0);
953 
954     void* ret = mmap(NULL, size, PROT_READ, MAP_SHARED, handle, offset);
955 
956     if (ret == MAP_FAILED)
957     {
958         set_system_error(error, errno);
959         ret = NULL;
960     }
961 
962     return ret;
963 }
964 
tr_sys_file_unmap(void const * address,uint64_t size,tr_error ** error)965 bool tr_sys_file_unmap(void const* address, uint64_t size, tr_error** error)
966 {
967     TR_ASSERT(address != NULL);
968     TR_ASSERT(size > 0);
969 
970     bool ret = munmap((void*)address, size) != -1;
971 
972     if (!ret)
973     {
974         set_system_error(error, errno);
975     }
976 
977     return ret;
978 }
979 
tr_sys_file_lock(tr_sys_file_t handle,int operation,tr_error ** error)980 bool tr_sys_file_lock(tr_sys_file_t handle, int operation, tr_error** error)
981 {
982     TR_ASSERT(handle != TR_BAD_SYS_FILE);
983     TR_ASSERT((operation & ~(TR_SYS_FILE_LOCK_SH | TR_SYS_FILE_LOCK_EX | TR_SYS_FILE_LOCK_NB | TR_SYS_FILE_LOCK_UN)) == 0);
984     TR_ASSERT(!!(operation & TR_SYS_FILE_LOCK_SH) + !!(operation & TR_SYS_FILE_LOCK_EX) +
985         !!(operation & TR_SYS_FILE_LOCK_UN) == 1);
986 
987     bool ret;
988 
989 #if defined(F_OFD_SETLK)
990 
991     struct flock fl = { 0 };
992 
993     switch (operation & (TR_SYS_FILE_LOCK_SH | TR_SYS_FILE_LOCK_EX | TR_SYS_FILE_LOCK_UN))
994     {
995     case TR_SYS_FILE_LOCK_SH:
996         fl.l_type = F_RDLCK;
997         break;
998 
999     case TR_SYS_FILE_LOCK_EX:
1000         fl.l_type = F_WRLCK;
1001         break;
1002 
1003     case TR_SYS_FILE_LOCK_UN:
1004         fl.l_type = F_UNLCK;
1005         break;
1006     }
1007 
1008     fl.l_whence = SEEK_SET;
1009 
1010     do
1011     {
1012         ret = fcntl(handle, (operation & TR_SYS_FILE_LOCK_NB) != 0 ? F_OFD_SETLK : F_OFD_SETLKW, &fl) != -1;
1013     }
1014     while (!ret && errno == EINTR);
1015 
1016     if (!ret && errno == EAGAIN)
1017     {
1018         errno = EWOULDBLOCK;
1019     }
1020 
1021 #elif defined(HAVE_FLOCK)
1022 
1023     int native_operation = 0;
1024 
1025     if ((operation & TR_SYS_FILE_LOCK_SH) != 0)
1026     {
1027         native_operation |= LOCK_SH;
1028     }
1029 
1030     if ((operation & TR_SYS_FILE_LOCK_EX) != 0)
1031     {
1032         native_operation |= LOCK_EX;
1033     }
1034 
1035     if ((operation & TR_SYS_FILE_LOCK_NB) != 0)
1036     {
1037         native_operation |= LOCK_NB;
1038     }
1039 
1040     if ((operation & TR_SYS_FILE_LOCK_UN) != 0)
1041     {
1042         native_operation |= LOCK_UN;
1043     }
1044 
1045     do
1046     {
1047         ret = flock(handle, native_operation) != -1;
1048     }
1049     while (!ret && errno == EINTR);
1050 
1051 #else
1052 
1053     (void)handle;
1054     (void)operation;
1055 
1056     errno = ENOSYS;
1057     ret = false;
1058 
1059 #endif
1060 
1061     if (!ret)
1062     {
1063         set_system_error(error, errno);
1064     }
1065 
1066     return ret;
1067 }
1068 
tr_sys_dir_get_current(tr_error ** error)1069 char* tr_sys_dir_get_current(tr_error** error)
1070 {
1071     char* ret;
1072 
1073     ret = getcwd(NULL, 0);
1074 
1075     if (ret == NULL && (errno == EINVAL || errno == ERANGE))
1076     {
1077         size_t size = PATH_MAX;
1078         char* tmp = NULL;
1079 
1080         do
1081         {
1082             tmp = tr_renew(char, tmp, size);
1083 
1084             if (tmp == NULL)
1085             {
1086                 break;
1087             }
1088 
1089             ret = getcwd(tmp, size);
1090             size += 2048;
1091         }
1092         while (ret == NULL && errno == ERANGE);
1093 
1094         if (ret == NULL)
1095         {
1096             int const err = errno;
1097             tr_free(tmp);
1098             errno = err;
1099         }
1100     }
1101 
1102     if (ret == NULL)
1103     {
1104         set_system_error(error, errno);
1105     }
1106 
1107     return ret;
1108 }
1109 
tr_sys_dir_create(char const * path,int flags,int permissions,tr_error ** error)1110 bool tr_sys_dir_create(char const* path, int flags, int permissions, tr_error** error)
1111 {
1112     TR_ASSERT(path != NULL);
1113 
1114     bool ret;
1115     tr_error* my_error = NULL;
1116 
1117     if ((flags & TR_SYS_DIR_CREATE_PARENTS) != 0)
1118     {
1119 #ifdef HAVE_MKDIRP
1120         ret = mkdirp(path, permissions) != -1;
1121 #else
1122         ret = create_path(path, permissions, &my_error);
1123 #endif
1124     }
1125     else
1126     {
1127         ret = mkdir(path, permissions) != -1;
1128     }
1129 
1130     if (!ret && errno == EEXIST)
1131     {
1132         struct stat sb;
1133 
1134         if (stat(path, &sb) != -1 && S_ISDIR(sb.st_mode))
1135         {
1136             tr_error_clear(&my_error);
1137             ret = true;
1138         }
1139         else
1140         {
1141             errno = EEXIST;
1142         }
1143     }
1144 
1145     if (!ret)
1146     {
1147         if (my_error != NULL)
1148         {
1149             tr_error_propagate(error, &my_error);
1150         }
1151         else
1152         {
1153             set_system_error(error, errno);
1154         }
1155     }
1156 
1157     return ret;
1158 }
1159 
tr_sys_dir_create_temp(char * path_template,tr_error ** error)1160 bool tr_sys_dir_create_temp(char* path_template, tr_error** error)
1161 {
1162     TR_ASSERT(path_template != NULL);
1163 
1164     bool ret;
1165 
1166 #ifdef HAVE_MKDTEMP
1167 
1168     ret = mkdtemp(path_template) != NULL;
1169 
1170 #else
1171 
1172     ret = mktemp(path_template) != NULL && mkdir(path_template, 0700) != -1;
1173 
1174 #endif
1175 
1176     if (!ret)
1177     {
1178         set_system_error(error, errno);
1179     }
1180 
1181     return ret;
1182 }
1183 
tr_sys_dir_open(char const * path,tr_error ** error)1184 tr_sys_dir_t tr_sys_dir_open(char const* path, tr_error** error)
1185 {
1186     TR_ASSERT(path != NULL);
1187 
1188     DIR* ret = opendir(path);
1189 
1190     if (ret == NULL)
1191     {
1192         set_system_error(error, errno);
1193         return TR_BAD_SYS_DIR;
1194     }
1195 
1196     return (tr_sys_dir_t)ret;
1197 }
1198 
tr_sys_dir_read_name(tr_sys_dir_t handle,tr_error ** error)1199 char const* tr_sys_dir_read_name(tr_sys_dir_t handle, tr_error** error)
1200 {
1201     TR_ASSERT(handle != TR_BAD_SYS_DIR);
1202 
1203     char const* ret = NULL;
1204 
1205     errno = 0;
1206     struct dirent* entry = readdir((DIR*)handle);
1207 
1208     if (entry != NULL)
1209     {
1210         ret = entry->d_name;
1211     }
1212     else if (errno != 0)
1213     {
1214         set_system_error(error, errno);
1215     }
1216 
1217     return ret;
1218 }
1219 
tr_sys_dir_close(tr_sys_dir_t handle,tr_error ** error)1220 bool tr_sys_dir_close(tr_sys_dir_t handle, tr_error** error)
1221 {
1222     TR_ASSERT(handle != TR_BAD_SYS_DIR);
1223 
1224     bool ret = closedir((DIR*)handle) != -1;
1225 
1226     if (!ret)
1227     {
1228         set_system_error(error, errno);
1229     }
1230 
1231     return ret;
1232 }
1233