Пример #1
0
/**
 * returns 0 on success, or an errno value on failure.
 * errno values include ENOENT if the parent folder doesn't exist,
 * plus the errno values set by tr_mkdirp () and open ().
 */
static int
cached_file_open (struct tr_cached_file  * o,
                  const char             * filename,
                  bool                     writable,
                  tr_preallocation_mode    allocation,
                  uint64_t                 file_size)
{
    int flags;
    struct stat sb;
    bool alreadyExisted;

    /* create subfolders, if any */
    if (writable)
    {
        char * dir = tr_dirname (filename);
        const int err = tr_mkdirp (dir, 0777) ? errno : 0;
        if (err) {
            tr_err (_("Couldn't create \"%1$s\": %2$s"), dir, tr_strerror (err));
            tr_free (dir);
            return err;
        }
        tr_free (dir);
    }

    alreadyExisted = !stat (filename, &sb) && S_ISREG (sb.st_mode);

    if (writable && !alreadyExisted && (allocation == TR_PREALLOCATE_FULL))
        if (preallocate_file_full (filename, file_size))
            tr_dbg ("Preallocated file \"%s\"", filename);

    /* open the file */
    flags = writable ? (O_RDWR | O_CREAT) : O_RDONLY;
    flags |= O_LARGEFILE | O_BINARY | O_SEQUENTIAL;
    o->fd = open (filename, flags, 0666);

    if (o->fd == -1)
    {
        const int err = errno;
        tr_err (_("Couldn't open \"%1$s\": %2$s"), filename, tr_strerror (err));
        return err;
    }

    /* If the file already exists and it's too large, truncate it.
     * This is a fringe case that happens if a torrent's been updated
     * and one of the updated torrent's files is smaller.
     * http://trac.transmissionbt.com/ticket/2228
     * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249
     */
    if (alreadyExisted && (file_size < (uint64_t)sb.st_size))
    {
        if (ftruncate (o->fd, file_size) == -1)
        {
            const int err = errno;
            tr_err (_("Couldn't truncate \"%1$s\": %2$s"), filename, tr_strerror (err));
            return err;
        }
    }

    if (writable && !alreadyExisted && (allocation == TR_PREALLOCATE_SPARSE))
        preallocate_file_sparse (o->fd, file_size);

    /* Many (most?) clients request blocks in ascending order,
     * so increase the readahead buffer.
     * Also, disable OS-level caching because "inactive memory" angers users. */
    tr_set_file_for_single_pass (o->fd);

    return 0;
}
Пример #2
0
/**
 * returns 0 on success, or an errno value on failure.
 * errno values include ENOENT if the parent folder doesn't exist,
 * plus the errno values set by tr_sys_dir_create () and tr_sys_file_open ().
 */
static int
cached_file_open (struct tr_cached_file  * o,
                  const char             * filename,
                  bool                     writable,
                  tr_preallocation_mode    allocation,
                  uint64_t                 file_size)
{
  int flags;
  tr_sys_path_info info;
  bool already_existed;
  bool resize_needed;
  tr_sys_file_t fd = TR_BAD_SYS_FILE;
  tr_error * error = NULL;

  /* create subfolders, if any */
  if (writable)
    {
      char * dir = tr_sys_path_dirname (filename, NULL);
      if (!tr_sys_dir_create (dir, TR_SYS_DIR_CREATE_PARENTS, 0777, &error))
        {
          tr_logAddError (_("Couldn't create \"%1$s\": %2$s"), dir, error->message);
          tr_free (dir);
          goto fail;
        }
      tr_free (dir);
    }

  already_existed = tr_sys_path_get_info (filename, 0, &info, NULL) && info.type == TR_SYS_PATH_IS_FILE;

  /* we can't resize the file w/o write permissions */
  resize_needed = already_existed && (file_size < info.size);
  writable |= resize_needed;

  /* open the file */
  flags = writable ? (TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE) : 0;
  flags |= TR_SYS_FILE_READ | TR_SYS_FILE_SEQUENTIAL;
  fd = tr_sys_file_open (filename, flags, 0666, &error);

  if (fd == TR_BAD_SYS_FILE)
    {
      tr_logAddError (_("Couldn't open \"%1$s\": %2$s"), filename, error->message);
      goto fail;
    }

  if (writable && !already_existed && allocation != TR_PREALLOCATE_NONE)
    {
      bool success = false;
      const char * type = NULL;

      if (allocation == TR_PREALLOCATE_FULL)
        {
          success = preallocate_file_full (fd, file_size, &error);
          type = _("full");
        }
      else if (allocation == TR_PREALLOCATE_SPARSE)
        {
          success = preallocate_file_sparse (fd, file_size, &error);
          type = _("sparse");
        }

      assert (type != NULL);

      if (!success)
        {
          tr_logAddError (_("Couldn't preallocate file \"%1$s\" (%2$s, size: %3$"PRIu64"): %4$s"),
            filename, type, file_size, error->message);
          goto fail;
        }

      tr_logAddDebug (_("Preallocated file \"%1$s\" (%2$s, size: %3$"PRIu64")"), filename, type, file_size);
    }

  /* If the file already exists and it's too large, truncate it.
   * This is a fringe case that happens if a torrent's been updated
   * and one of the updated torrent's files is smaller.
   * http://trac.transmissionbt.com/ticket/2228
   * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249
   */
  if (resize_needed && !tr_sys_file_truncate (fd, file_size, &error))
    {
      tr_logAddError (_("Couldn't truncate \"%1$s\": %2$s"), filename, error->message);
      goto fail;
    }

  o->fd = fd;
  return 0;

fail:
  {
    const int err = error->code;
    tr_error_free (error);

    if (fd != TR_BAD_SYS_FILE)
      tr_sys_file_close (fd, NULL);

    return err;
  }
}