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