source: trunk/libtransmission/file-posix.c @ 14321

Last change on this file since 14321 was 14321, checked in by jordan, 8 years ago

(trunk, libT) #4160 'foreign character support' -- merge mike.dld's 4160-03a-file.platch, which introduces tr_sys_file_*() portability wrappers

File size: 16.4 KB
Line 
1/*
2 * This file Copyright (C) 2013-2014 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 * $Id$
8 */
9
10#if (defined (HAVE_POSIX_FADVISE) || defined (HAVE_POSIX_FALLOCATE)) && (!defined (_XOPEN_SOURCE) || _XOPEN_SOURCE < 600)
11 #ifdef _XOPEN_SOURCE
12  #undef _XOPEN_SOURCE
13 #endif
14 #define _XOPEN_SOURCE 600
15#endif
16
17#if (defined (HAVE_FALLOCATE64) || defined (HAVE_CANONICALIZE_FILE_NAME)) && !defined (_GNU_SOURCE)
18 #define _GNU_SOURCE
19#endif
20
21#if defined (__APPLE__) && !defined (_DARWIN_C_SOURCE)
22 #define _DARWIN_C_SOURCE
23#endif
24
25#include <assert.h>
26#include <errno.h>
27#include <fcntl.h> /* O_LARGEFILE, posix_fadvise (), [posix_]fallocate () */
28#include <libgen.h> /* basename (), dirname () */
29#include <limits.h> /* PATH_MAX */
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <sys/mman.h> /* mmap (), munmap () */
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <unistd.h> /* lseek (), write (), ftruncate (), pread (), pwrite (), pathconf (), etc */
37
38#ifdef HAVE_XFS_XFS_H
39 #include <xfs/xfs.h>
40#endif
41
42#include "transmission.h"
43#include "file.h"
44#include "platform.h"
45#include "utils.h"
46
47#ifndef O_LARGEFILE
48 #define O_LARGEFILE 0
49#endif
50#ifndef O_BINARY
51 #define O_BINARY 0
52#endif
53#ifndef O_SEQUENTIAL
54 #define O_SEQUENTIAL 0
55#endif
56#ifndef O_CLOEXEC
57 #define O_CLOEXEC 0
58#endif
59
60#ifndef PATH_MAX
61 #define PATH_MAX 4096
62#endif
63
64/* don't use pread/pwrite on old versions of uClibc because they're buggy.
65 * https://trac.transmissionbt.com/ticket/3826 */
66#ifdef __UCLIBC__
67#define TR_UCLIBC_CHECK_VERSION(major,minor,micro) \
68  (__UCLIBC_MAJOR__ > (major) || \
69   (__UCLIBC_MAJOR__ == (major) && __UCLIBC_MINOR__ > (minor)) || \
70   (__UCLIBC_MAJOR__ == (major) && __UCLIBC_MINOR__ == (minor) && \
71      __UCLIBC_SUBLEVEL__ >= (micro)))
72#if !TR_UCLIBC_CHECK_VERSION (0,9,28)
73 #undef HAVE_PREAD
74 #undef HAVE_PWRITE
75#endif
76#endif
77
78#ifdef __APPLE__
79 #ifndef HAVE_PREAD
80  #define HAVE_PREAD
81 #endif
82 #ifndef HAVE_PWRITE
83  #define HAVE_PWRITE
84 #endif
85 #ifndef HAVE_MKDTEMP
86  #define HAVE_MKDTEMP
87 #endif
88#endif
89
90static void
91set_system_error (tr_error ** error,
92                  int         code)
93{
94  if (error == NULL)
95    return;
96
97  tr_error_set_literal (error, code, tr_strerror (code));
98}
99
100static void
101set_system_error_if_file_found (tr_error ** error,
102                                int         code)
103{
104  if (code != ENOENT)
105    set_system_error (error, code);
106}
107
108static void
109stat_to_sys_path_info (const struct stat * sb,
110                       tr_sys_path_info  * info)
111{
112  if (S_ISREG (sb->st_mode))
113    info->type = TR_SYS_PATH_IS_FILE;
114  else if (S_ISDIR (sb->st_mode))
115    info->type = TR_SYS_PATH_IS_DIRECTORY;
116  else
117    info->type = TR_SYS_PATH_IS_OTHER;
118
119  info->size = (uint64_t) sb->st_size;
120  info->last_modified_at = sb->st_mtime;
121}
122
123static void
124set_file_for_single_pass (tr_sys_file_t handle)
125{
126  /* Set hints about the lookahead buffer and caching. It's okay
127     for these to fail silently, so don't let them affect errno */
128
129  const int err = errno;
130
131  if (handle == TR_BAD_SYS_FILE)
132    return;
133
134#ifdef HAVE_POSIX_FADVISE
135
136  posix_fadvise (handle, 0, 0, POSIX_FADV_SEQUENTIAL);
137
138#endif
139
140#ifdef __APPLE__
141
142  fcntl (handle, F_RDAHEAD, 1);
143  fcntl (handle, F_NOCACHE, 1);
144
145#endif
146
147  errno = err;
148}
149
150bool
151tr_sys_path_exists (const char  * path,
152                    tr_error   ** error)
153{
154  bool ret;
155
156  assert (path != NULL);
157
158  ret = access (path, F_OK) != -1;
159
160  if (!ret)
161    set_system_error_if_file_found (error, errno);
162
163  return ret;
164}
165
166bool
167tr_sys_path_get_info (const char        * path,
168                      int                 flags,
169                      tr_sys_path_info  * info,
170                      tr_error         ** error)
171{
172  bool ret;
173  struct stat sb;
174
175  assert (path != NULL);
176  assert (info != NULL);
177
178  if ((flags & TR_SYS_PATH_NO_FOLLOW) == 0)
179    ret = stat (path, &sb) != -1;
180  else
181    ret = lstat (path, &sb) != -1;
182
183  if (ret)
184    stat_to_sys_path_info (&sb, info);
185  else
186    set_system_error (error, errno);
187
188  return ret;
189}
190
191bool
192tr_sys_path_is_same (const char  * path1,
193                     const char  * path2,
194                     tr_error   ** error)
195{
196  bool ret = false;
197  struct stat sb1, sb2;
198
199  assert (path1 != NULL);
200  assert (path2 != NULL);
201
202  if (stat (path1, &sb1) != -1 && stat (path2, &sb2) != -1)
203    ret = sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino;
204  else
205    set_system_error_if_file_found (error, errno);
206
207  return ret;
208}
209
210char *
211tr_sys_path_resolve (const char  * path,
212                     tr_error   ** error)
213{
214  char * ret = NULL;
215  char * tmp = NULL;
216
217  assert (path != NULL);
218
219#if defined (HAVE_CANONICALIZE_FILE_NAME)
220
221  ret = canonicalize_file_name (path);
222
223#endif
224
225#if defined (_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L
226
227  /* Better safe than sorry: realpath () officially supports NULL as destination
228     starting off POSIX.1-2008. */
229
230  if (ret == NULL)
231    ret = realpath (path, NULL);
232
233#endif
234
235  if (ret == NULL)
236    {
237      tmp = tr_new (char, PATH_MAX);
238      ret = realpath (path, tmp);
239      if (ret != NULL)
240        ret = tr_strdup (ret);
241    }
242
243  if (ret == NULL)
244    set_system_error (error, errno);
245
246  tr_free (tmp);
247
248  return ret;
249}
250
251char *
252tr_sys_path_basename (const char  * path,
253                      tr_error   ** error)
254{
255  char * ret = NULL;
256  char * tmp;
257
258  assert (path != NULL);
259
260  tmp = tr_strdup (path);
261  ret = basename (tmp);
262  if (ret != NULL)
263    ret = tr_strdup (ret);
264  else
265    set_system_error (error, errno);
266
267  tr_free (tmp);
268
269  return ret;
270}
271
272char *
273tr_sys_path_dirname (const char  * path,
274                     tr_error   ** error)
275{
276  char * ret = NULL;
277  char * tmp;
278
279  assert (path != NULL);
280
281  tmp = tr_strdup (path);
282  ret = dirname (tmp);
283  if (ret != NULL)
284    ret = tr_strdup (ret);
285  else
286    set_system_error (error, errno);
287
288  tr_free (tmp);
289
290  return ret;
291}
292
293bool
294tr_sys_path_rename (const char  * src_path,
295                    const char  * dst_path,
296                    tr_error   ** error)
297{
298  bool ret;
299
300  assert (src_path != NULL);
301  assert (dst_path != NULL);
302
303  ret = rename (src_path, dst_path) != -1;
304
305  if (!ret)
306    set_system_error (error, errno);
307
308  return ret;
309}
310
311bool
312tr_sys_path_remove (const char  * path,
313                    tr_error   ** error)
314{
315  bool ret;
316
317  assert (path != NULL);
318
319  ret = remove (path) != -1;
320
321  if (!ret)
322    set_system_error (error, errno);
323
324  return ret;
325}
326
327tr_sys_file_t
328tr_sys_file_get_std (tr_std_sys_file_t    std_file,
329                     tr_error          ** error)
330{
331  tr_sys_file_t ret = TR_BAD_SYS_FILE;
332
333  switch (std_file)
334    {
335    case TR_STD_SYS_FILE_IN:
336      ret = STDIN_FILENO;
337      break;
338    case TR_STD_SYS_FILE_OUT:
339      ret = STDOUT_FILENO;
340      break;
341    case TR_STD_SYS_FILE_ERR:
342      ret = STDERR_FILENO;
343      break;
344    default:
345      assert (0 && "Unknown standard file");
346      set_system_error (error, EINVAL);
347    }
348
349  return ret;
350}
351
352tr_sys_file_t
353tr_sys_file_open (const char  * path,
354                  int           flags,
355                  int           permissions,
356                  tr_error   ** error)
357{
358  tr_sys_file_t ret;
359  int native_flags = 0;
360
361  assert (path != NULL);
362  assert ((flags & (TR_SYS_FILE_READ | TR_SYS_FILE_WRITE)) != 0);
363
364  if ((flags & (TR_SYS_FILE_READ | TR_SYS_FILE_WRITE)) == (TR_SYS_FILE_READ | TR_SYS_FILE_WRITE))
365    native_flags |= O_RDWR;
366  else if (flags & TR_SYS_FILE_READ)
367    native_flags |= O_RDONLY;
368  else if (flags & TR_SYS_FILE_WRITE)
369    native_flags |= O_WRONLY;
370
371  native_flags |=
372    (flags & TR_SYS_FILE_CREATE ? O_CREAT : 0) |
373    (flags & TR_SYS_FILE_CREATE_NEW ? O_CREAT | O_EXCL : 0) |
374    (flags & TR_SYS_FILE_APPEND ? O_APPEND : 0) |
375    (flags & TR_SYS_FILE_TRUNCATE ? O_TRUNC : 0) |
376    (flags & TR_SYS_FILE_SEQUENTIAL ? O_SEQUENTIAL : 0) |
377    O_BINARY | O_LARGEFILE | O_CLOEXEC;
378
379  ret = open (path, native_flags, permissions);
380
381  if (ret != TR_BAD_SYS_FILE)
382    {
383      if (flags & TR_SYS_FILE_SEQUENTIAL)
384        set_file_for_single_pass (ret);
385    }
386  else
387    {
388      set_system_error (error, errno);
389    }
390
391  return ret;
392}
393
394tr_sys_file_t
395tr_sys_file_open_temp (char      * path_template,
396                       tr_error ** error)
397{
398  tr_sys_file_t ret;
399
400  assert (path_template != NULL);
401
402  ret = mkstemp (path_template);
403
404  if (ret == TR_BAD_SYS_FILE)
405    set_system_error (error, errno);
406
407  set_file_for_single_pass (ret);
408
409  return ret;
410}
411
412bool
413tr_sys_file_close (tr_sys_file_t    handle,
414                   tr_error      ** error)
415{
416  bool ret;
417
418  assert (handle != TR_BAD_SYS_FILE);
419
420  ret = close (handle) != -1;
421
422  if (!ret)
423    set_system_error (error, errno);
424
425  return ret;
426}
427
428bool
429tr_sys_file_get_info (tr_sys_file_t       handle,
430                      tr_sys_path_info  * info,
431                      tr_error         ** error)
432{
433  bool ret;
434  struct stat sb;
435
436  assert (handle != TR_BAD_SYS_FILE);
437  assert (info != NULL);
438
439  ret = fstat (handle, &sb) != -1;
440
441  if (ret)
442    stat_to_sys_path_info (&sb, info);
443  else
444    set_system_error (error, errno);
445
446  return ret;
447}
448
449bool
450tr_sys_file_seek (tr_sys_file_t       handle,
451                  int64_t             offset,
452                  tr_seek_origin_t    origin,
453                  uint64_t          * new_offset,
454                  tr_error         ** error)
455{
456  bool ret = false;
457  off_t my_new_offset;
458
459  TR_STATIC_ASSERT (TR_SEEK_SET == SEEK_SET, "values should match");
460  TR_STATIC_ASSERT (TR_SEEK_CUR == SEEK_CUR, "values should match");
461  TR_STATIC_ASSERT (TR_SEEK_END == SEEK_END, "values should match");
462
463  TR_STATIC_ASSERT (sizeof (*new_offset) >= sizeof (my_new_offset), "");
464
465  assert (handle != TR_BAD_SYS_FILE);
466  assert (origin == TR_SEEK_SET || origin == TR_SEEK_CUR || origin == TR_SEEK_END);
467
468  my_new_offset = lseek (handle, offset, origin);
469
470  if (my_new_offset != (off_t)-1)
471    {
472      if (new_offset != NULL)
473        *new_offset = my_new_offset;
474      ret = true;
475    }
476  else
477    {
478      set_system_error (error, errno);
479    }
480
481  return ret;
482}
483
484bool
485tr_sys_file_read (tr_sys_file_t    handle,
486                  void           * buffer,
487                  uint64_t         size,
488                  uint64_t       * bytes_read,
489                  tr_error      ** error)
490{
491  bool ret = false;
492  ssize_t my_bytes_read;
493
494  TR_STATIC_ASSERT (sizeof (*bytes_read) >= sizeof (my_bytes_read), "");
495
496  assert (handle != TR_BAD_SYS_FILE);
497  assert (buffer != NULL || size == 0);
498
499  my_bytes_read = read (handle, buffer, size);
500
501  if (my_bytes_read != -1)
502    {
503      if (bytes_read != NULL)
504        *bytes_read = my_bytes_read;
505      ret = true;
506    }
507  else
508    {
509      set_system_error (error, errno);
510    }
511
512  return ret;
513}
514
515bool
516tr_sys_file_read_at (tr_sys_file_t    handle,
517                     void           * buffer,
518                     uint64_t         size,
519                     uint64_t         offset,
520                     uint64_t       * bytes_read,
521                     tr_error      ** error)
522{
523  bool ret = false;
524  ssize_t my_bytes_read;
525
526  TR_STATIC_ASSERT (sizeof (*bytes_read) >= sizeof (my_bytes_read), "");
527
528  assert (handle != TR_BAD_SYS_FILE);
529  assert (buffer != NULL || size == 0);
530
531#ifdef HAVE_PREAD
532
533  my_bytes_read = pread (handle, buffer, size, offset);
534
535#else
536
537  if (lseek (handle, offset, SEEK_SET) != -1)
538    my_bytes_read = read (handle, buffer, size);
539  else
540    my_bytes_read = -1;
541
542#endif
543
544  if (my_bytes_read != -1)
545    {
546      if (bytes_read != NULL)
547        *bytes_read = my_bytes_read;
548      ret = true;
549    }
550  else
551    {
552      set_system_error (error, errno);
553    }
554
555  return ret;
556}
557
558bool
559tr_sys_file_write (tr_sys_file_t    handle,
560                   const void     * buffer,
561                   uint64_t         size,
562                   uint64_t       * bytes_written,
563                   tr_error      ** error)
564{
565  bool ret = false;
566  ssize_t my_bytes_written;
567
568  TR_STATIC_ASSERT (sizeof (*bytes_written) >= sizeof (my_bytes_written), "");
569
570  assert (handle != TR_BAD_SYS_FILE);
571  assert (buffer != NULL || size == 0);
572
573  my_bytes_written = write (handle, buffer, size);
574
575  if (my_bytes_written != -1)
576    {
577      if (bytes_written != NULL)
578        *bytes_written = my_bytes_written;
579      ret = true;
580    }
581  else
582    {
583      set_system_error (error, errno);
584    }
585
586  return ret;
587}
588
589bool
590tr_sys_file_write_at (tr_sys_file_t    handle,
591                      const void     * buffer,
592                      uint64_t         size,
593                      uint64_t         offset,
594                      uint64_t       * bytes_written,
595                      tr_error      ** error)
596{
597  bool ret = false;
598  ssize_t my_bytes_written;
599
600  TR_STATIC_ASSERT (sizeof (*bytes_written) >= sizeof (my_bytes_written), "");
601
602  assert (handle != TR_BAD_SYS_FILE);
603  assert (buffer != NULL || size == 0);
604
605#ifdef HAVE_PWRITE
606
607  my_bytes_written = pwrite (handle, buffer, size, offset);
608
609#else
610
611  if (lseek (handle, offset, SEEK_SET) != -1)
612    my_bytes_written = write (handle, buffer, size);
613  else
614    my_bytes_written = -1;
615
616#endif
617
618  if (my_bytes_written != -1)
619    {
620      if (bytes_written != NULL)
621        *bytes_written = my_bytes_written;
622      ret = true;
623    }
624  else
625    {
626      set_system_error (error, errno);
627    }
628
629  return ret;
630}
631
632bool
633tr_sys_file_flush (tr_sys_file_t    handle,
634                   tr_error      ** error)
635{
636  bool ret;
637
638  assert (handle != TR_BAD_SYS_FILE);
639
640  ret = fsync (handle) != -1;
641
642  if (!ret)
643    set_system_error (error, errno);
644
645  return ret;
646}
647
648bool
649tr_sys_file_truncate (tr_sys_file_t    handle,
650                      uint64_t         size,
651                      tr_error      ** error)
652{
653  bool ret;
654
655  assert (handle != TR_BAD_SYS_FILE);
656
657  ret = ftruncate (handle, size) != -1;
658
659  if (!ret)
660    set_system_error (error, errno);
661
662  return ret;
663}
664
665bool
666tr_sys_file_prefetch (tr_sys_file_t    handle,
667                      uint64_t         offset,
668                      uint64_t         size,
669                      tr_error      ** error)
670{
671  bool ret = false;
672
673#if defined (HAVE_POSIX_FADVISE)
674
675  int code;
676
677  assert (handle != TR_BAD_SYS_FILE);
678  assert (size > 0);
679
680  code = posix_fadvise (handle, offset, size, POSIX_FADV_WILLNEED);
681
682  if (code == 0)
683    ret = true;
684  else
685    set_system_error (error, code);
686
687#elif defined (__APPLE__)
688
689  struct radvisory radv;
690
691  assert (handle != TR_BAD_SYS_FILE);
692  assert (size > 0);
693
694  radv.ra_offset = offset;
695  radv.ra_count = size;
696
697  ret = fcntl (handle, F_RDADVISE, &radv) != -1;
698
699  if (!ret)
700    set_system_error (error, errno);
701
702#endif
703
704  return ret;
705}
706
707bool
708tr_sys_file_preallocate (tr_sys_file_t    handle,
709                         uint64_t         size,
710                         int              flags,
711                         tr_error      ** error)
712{
713  bool ret = false;
714
715  assert (handle != TR_BAD_SYS_FILE);
716
717  errno = 0;
718
719#ifdef HAVE_FALLOCATE64
720
721  /* fallocate64 is always preferred, so try it first */
722  ret = fallocate64 (handle, 0, 0, size) != -1;
723
724#endif
725
726  if (!ret && (flags & TR_SYS_FILE_PREALLOC_SPARSE) == 0)
727    {
728      int code = errno;
729
730#ifdef HAVE_XFS_XFS_H
731
732      if (!ret && platform_test_xfs_fd (handle))
733        {
734          xfs_flock64_t fl;
735
736          fl.l_whence = 0;
737          fl.l_start = 0;
738          fl.l_len = size;
739
740          ret = xfsctl (NULL, handle, XFS_IOC_RESVSP64, &fl) != -1;
741
742          code = errno;
743        }
744
745#endif
746
747#ifdef __APPLE__
748
749      if (!ret)
750        {
751          fstore_t fst;
752
753          fst.fst_flags = F_ALLOCATECONTIG;
754          fst.fst_posmode = F_PEOFPOSMODE;
755          fst.fst_offset = 0;
756          fst.fst_length = size;
757          fst.fst_bytesalloc = 0;
758
759          ret = fcntl (handle, F_PREALLOCATE, &fst) != -1;
760
761          if (ret)
762            ret = ftruncate (handle, size) != -1;
763
764          code = errno;
765        }
766
767#endif
768
769#ifdef HAVE_POSIX_FALLOCATE
770
771      if (!ret)
772        {
773          code = posix_fallocate (handle, 0, size);
774          ret = code == 0;
775        }
776
777#endif
778
779      errno = code;
780    }
781
782  if (!ret)
783    set_system_error (error, errno);
784
785  return ret;
786}
787
788void *
789tr_sys_file_map_for_reading (tr_sys_file_t    handle,
790                             uint64_t         offset,
791                             uint64_t         size,
792                             tr_error      ** error)
793{
794  void * ret;
795
796  assert (handle != TR_BAD_SYS_FILE);
797  assert (size > 0);
798
799  ret = mmap (NULL, size, PROT_READ, MAP_SHARED, handle, offset);
800
801  if (ret == MAP_FAILED)
802    {
803      set_system_error (error, errno);
804      ret = NULL;
805    }
806
807  return ret;
808}
809
810bool
811tr_sys_file_unmap (const void  * address,
812                   uint64_t      size,
813                   tr_error   ** error)
814{
815  bool ret;
816
817  assert (address != NULL);
818  assert (size > 0);
819
820  ret = munmap ((void *) address, size) != -1;
821
822  if (!ret)
823    set_system_error (error, errno);
824
825  return ret;
826}
Note: See TracBrowser for help on using the repository browser.