/** * 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; }
/** * 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; } }