Changeset 14367


Ignore:
Timestamp:
Dec 10, 2014, 6:23:11 PM (8 years ago)
Author:
mikedld
Message:

#5369: Improve file allocation error checking (initial patch by g.proskurin)

Additionally,

  • always close file descriptor on error in cached_file_open (FD leak),
  • only store file descriptor to tr_cached_file on success,
  • call ftruncate after xfsctl-based preallocation so that correct size is reported by the system.
Location:
trunk/libtransmission
Files:
1 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/libtransmission/CMakeLists.txt

    r14358 r14367  
    7171set(${PROJECT_NAME}_PUBLIC_HEADERS
    7272    error.h
     73    error-types.h
    7374    file.h
    7475    log.h
  • trunk/libtransmission/Makefile.am

    r14358 r14367  
    9696  completion.h \
    9797  error.h \
     98  error-types.h \
    9899  fdlimit.h \
    99100  file.h \
  • trunk/libtransmission/fdlimit.c

    r14347 r14367  
    1818#include "transmission.h"
    1919#include "error.h"
     20#include "error-types.h"
    2021#include "fdlimit.h"
    2122#include "file.h"
     
    3940
    4041static bool
    41 preallocate_file_sparse (tr_sys_file_t fd, uint64_t length)
    42 {
    43   const char zero = '\0';
    44   bool success = false;
    45 
    46   if (!length)
    47     success = true;
    48 
    49   if (!success)
    50     success = tr_sys_file_preallocate (fd, length, TR_SYS_FILE_PREALLOC_SPARSE, NULL);
    51 
    52   if (!success) /* fallback: the old-style seek-and-write */
    53     {
    54       /* seek requires signed offset, so length should be in mod range */
    55       assert (length < 0x7FFFFFFFFFFFFFFFULL);
    56 
    57       success = tr_sys_file_seek (fd, length - 1, TR_SEEK_SET, NULL, NULL) &&
    58                 tr_sys_file_write (fd, &zero, 1, NULL, NULL) &&
    59                 tr_sys_file_truncate (fd, length, NULL);
    60     }
    61 
    62   return success;
     42preallocate_file_sparse (tr_sys_file_t fd, uint64_t length, tr_error ** error)
     43{
     44  tr_error * my_error = NULL;
     45
     46  if (length == 0)
     47    return true;
     48
     49  if (tr_sys_file_preallocate (fd, length, TR_SYS_FILE_PREALLOC_SPARSE, &my_error))
     50    return true;
     51
     52  dbgmsg ("Preallocating (sparse, normal) failed (%d): %s", my_error->code, my_error->message);
     53
     54  if (!TR_ERROR_IS_ENOSPC (my_error->code))
     55    {
     56      const char zero = '\0';
     57
     58      tr_error_clear (&my_error);
     59
     60      /* fallback: the old-style seek-and-write */
     61      if (tr_sys_file_write_at (fd, &zero, 1, length - 1, NULL, &my_error) &&
     62          tr_sys_file_truncate (fd, length, &my_error))
     63        return true;
     64
     65      dbgmsg ("Preallocating (sparse, fallback) failed (%d): %s", my_error->code, my_error->message);
     66    }
     67
     68  tr_error_propagate (error, &my_error);
     69  return false;
    6370}
    6471
    6572static bool
    66 preallocate_file_full (const char * filename, uint64_t length)
    67 {
    68   bool success = false;
    69 
    70   tr_sys_file_t fd = tr_sys_file_open (filename, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE, 0666, NULL);
    71   if (fd != TR_BAD_SYS_FILE)
    72     {
    73       success = tr_sys_file_preallocate (fd, length, 0, NULL);
    74 
    75       if (!success) /* if nothing else works, do it the old-fashioned way */
    76         {
    77           uint8_t buf[ 4096 ];
    78           memset (buf, 0, sizeof (buf));
    79           success = true;
    80           while (success && (length > 0))
    81             {
    82               const uint64_t thisPass = MIN (length, sizeof (buf));
    83               uint64_t bytes_written;
    84               success = tr_sys_file_write (fd, buf, thisPass, &bytes_written, NULL) && bytes_written == thisPass;
    85               length -= thisPass;
    86             }
    87         }
    88 
    89       tr_sys_file_close (fd, NULL);
    90     }
    91 
    92   return success;
     73preallocate_file_full (tr_sys_file_t fd, uint64_t length, tr_error ** error)
     74{
     75  tr_error * my_error = NULL;
     76
     77  if (length == 0)
     78    return true;
     79
     80  if (tr_sys_file_preallocate (fd, length, 0, &my_error))
     81    return true;
     82
     83  dbgmsg ("Preallocating (full, normal) failed (%d): %s", my_error->code, my_error->message);
     84
     85  if (!TR_ERROR_IS_ENOSPC (my_error->code))
     86    {
     87      uint8_t buf[4096];
     88      bool success = true;
     89
     90      memset (buf, 0, sizeof (buf));
     91      tr_error_clear (&my_error);
     92
     93      /* fallback: the old-fashioned way */
     94      while (success && length > 0)
     95        {
     96          const uint64_t thisPass = MIN (length, sizeof (buf));
     97          uint64_t bytes_written;
     98          success = tr_sys_file_write (fd, buf, thisPass, &bytes_written, &my_error);
     99          length -= bytes_written;
     100        }
     101
     102      if (success)
     103        return true;
     104
     105      dbgmsg ("Preallocating (full, fallback) failed (%d): %s", my_error->code, my_error->message);
     106    }
     107
     108  tr_error_propagate (error, &my_error);
     109  return false;
    93110}
    94111
     
    141158  bool already_existed;
    142159  bool resize_needed;
     160  tr_sys_file_t fd = TR_BAD_SYS_FILE;
    143161  tr_error * error = NULL;
    144162
     
    149167      if (!tr_sys_dir_create (dir, TR_SYS_DIR_CREATE_PARENTS, 0777, &error))
    150168        {
    151           const int err = error->code;
    152169          tr_logAddError (_("Couldn't create \"%1$s\": %2$s"), dir, error->message);
    153           tr_error_free (error);
    154170          tr_free (dir);
    155           return err;
     171          goto fail;
    156172        }
    157173      tr_free (dir);
     
    159175
    160176  already_existed = tr_sys_path_get_info (filename, 0, &info, NULL) && info.type == TR_SYS_PATH_IS_FILE;
    161 
    162   if (writable && !already_existed && (allocation == TR_PREALLOCATE_FULL))
    163     if (preallocate_file_full (filename, file_size))
    164       tr_logAddDebug ("Preallocated file \"%s\"", filename);
    165177
    166178  /* we can't resize the file w/o write permissions */
     
    171183  flags = writable ? (TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE) : 0;
    172184  flags |= TR_SYS_FILE_READ | TR_SYS_FILE_SEQUENTIAL;
    173   o->fd = tr_sys_file_open (filename, flags, 0666, &error);
    174 
    175   if (o->fd == TR_BAD_SYS_FILE)
    176     {
    177       const int err = error->code;
     185  fd = tr_sys_file_open (filename, flags, 0666, &error);
     186
     187  if (fd == TR_BAD_SYS_FILE)
     188    {
    178189      tr_logAddError (_("Couldn't open \"%1$s\": %2$s"), filename, error->message);
    179       tr_error_free (error);
    180       return err;
     190      goto fail;
     191    }
     192
     193  if (writable && !already_existed && allocation != TR_PREALLOCATE_NONE)
     194    {
     195      bool success = false;
     196      const char * type = NULL;
     197
     198      if (allocation == TR_PREALLOCATE_FULL)
     199        {
     200          success = preallocate_file_full (fd, file_size, &error);
     201          type = _("full");
     202        }
     203      else if (allocation == TR_PREALLOCATE_SPARSE)
     204        {
     205          success = preallocate_file_sparse (fd, file_size, &error);
     206          type = _("sparse");
     207        }
     208
     209      assert (type != NULL);
     210
     211      if (!success)
     212        {
     213          tr_logAddError (_("Couldn't preallocate file \"%1$s\" (%2$s, size: %3$"PRIu64"): %4$s"),
     214            filename, type, file_size, error->message);
     215          goto fail;
     216        }
     217
     218      tr_logAddDebug (_("Preallocated file \"%1$s\" (%2$s, size: %3$"PRIu64")"), filename, type, file_size);
    181219    }
    182220
     
    187225   * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249
    188226   */
    189   if (resize_needed && !tr_sys_file_truncate (o->fd, file_size, &error))
    190     {
    191       const int err = error->code;
     227  if (resize_needed && !tr_sys_file_truncate (fd, file_size, &error))
     228    {
    192229      tr_logAddError (_("Couldn't truncate \"%1$s\": %2$s"), filename, error->message);
    193       tr_error_free (error);
    194       return err;
    195     }
    196 
    197   if (writable && !already_existed && (allocation == TR_PREALLOCATE_SPARSE))
    198     preallocate_file_sparse (o->fd, file_size);
    199 
     230      goto fail;
     231    }
     232
     233  o->fd = fd;
    200234  return 0;
     235
     236fail:
     237  {
     238    const int err = error->code;
     239    tr_error_free (error);
     240
     241    if (fd != TR_BAD_SYS_FILE)
     242      tr_sys_file_close (fd, NULL);
     243
     244    return err;
     245  }
    201246}
    202247
  • trunk/libtransmission/file-posix.c

    r14330 r14367  
    608608  assert (handle != TR_BAD_SYS_FILE);
    609609  assert (buffer != NULL || size == 0);
     610  /* seek requires signed offset, so it should be in mod range */
     611  assert (offset < UINT64_MAX / 2);
    610612
    611613#ifdef HAVE_PREAD
     
    682684  assert (handle != TR_BAD_SYS_FILE);
    683685  assert (buffer != NULL || size == 0);
     686  /* seek requires signed offset, so it should be in mod range */
     687  assert (offset < UINT64_MAX / 2);
    684688
    685689#ifdef HAVE_PWRITE
     
    802806  ret = fallocate64 (handle, 0, 0, size) != -1;
    803807
    804 #endif
    805 
    806   if (!ret && (flags & TR_SYS_FILE_PREALLOC_SPARSE) == 0)
     808  if (ret || errno == ENOSPC)
     809    goto out;
     810
     811#endif
     812
     813  if ((flags & TR_SYS_FILE_PREALLOC_SPARSE) == 0)
    807814    {
    808815      int code = errno;
     
    810817#ifdef HAVE_XFS_XFS_H
    811818
    812       if (!ret && platform_test_xfs_fd (handle))
     819      if (platform_test_xfs_fd (handle))
    813820        {
    814821          xfs_flock64_t fl;
     
    820827          ret = xfsctl (NULL, handle, XFS_IOC_RESVSP64, &fl) != -1;
    821828
    822           code = errno;
    823         }
    824 
    825 #endif
    826 
    827 #ifdef __APPLE__
    828 
    829       if (!ret)
    830         {
    831           fstore_t fst;
    832 
    833           fst.fst_flags = F_ALLOCATECONTIG;
    834           fst.fst_posmode = F_PEOFPOSMODE;
    835           fst.fst_offset = 0;
    836           fst.fst_length = size;
    837           fst.fst_bytesalloc = 0;
    838 
    839           ret = fcntl (handle, F_PREALLOCATE, &fst) != -1;
    840 
    841829          if (ret)
    842830            ret = ftruncate (handle, size) != -1;
    843831
    844832          code = errno;
     833
     834          if (ret || code == ENOSPC)
     835            goto non_sparse_out;
    845836        }
    846837
    847838#endif
    848839
     840#ifdef __APPLE__
     841
     842      {
     843        fstore_t fst;
     844
     845        fst.fst_flags = F_ALLOCATEALL;
     846        fst.fst_posmode = F_PEOFPOSMODE;
     847        fst.fst_offset = 0;
     848        fst.fst_length = size;
     849        fst.fst_bytesalloc = 0;
     850
     851        ret = fcntl (handle, F_PREALLOCATE, &fst) != -1;
     852
     853        if (ret)
     854          ret = ftruncate (handle, size) != -1;
     855
     856        code = errno;
     857
     858        if (ret || code == ENOSPC)
     859          goto non_sparse_out;
     860      }
     861
     862#endif
     863
    849864#ifdef HAVE_POSIX_FALLOCATE
    850865
    851       if (!ret)
    852         {
    853           code = posix_fallocate (handle, 0, size);
    854           ret = code == 0;
    855         }
    856 
    857 #endif
    858 
     866      code = posix_fallocate (handle, 0, size);
     867      ret = code == 0;
     868
     869#endif
     870
     871non_sparse_out:
    859872      errno = code;
    860873    }
    861874
     875out:
    862876  if (!ret)
    863877    set_system_error (error, errno);
Note: See TracChangeset for help on using the changeset viewer.