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

Last change on this file since 14584 was 14584, checked in by mikedld, 7 years ago

#5802: Don't make assumptions of remote path validity in transmission-remote

Defer validity checks until path gets to the remote side, where they
actually make sense. Add simple checks for download directory path to
ensure it's not relative, since one cannot know what current working
directory of the remote process is.

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