static bool preallocate_file_sparse (tr_sys_file_t fd, uint64_t length, tr_error ** error) { tr_error * my_error = NULL; if (length == 0) return true; if (tr_sys_file_preallocate (fd, length, TR_SYS_FILE_PREALLOC_SPARSE, &my_error)) return true; dbgmsg ("Preallocating (sparse, normal) failed (%d): %s", my_error->code, my_error->message); if (!TR_ERROR_IS_ENOSPC (my_error->code)) { const char zero = '\0'; tr_error_clear (&my_error); /* fallback: the old-style seek-and-write */ if (tr_sys_file_write_at (fd, &zero, 1, length - 1, NULL, &my_error) && tr_sys_file_truncate (fd, length, &my_error)) return true; dbgmsg ("Preallocating (sparse, fallback) failed (%d): %s", my_error->code, my_error->message); } tr_error_propagate (error, &my_error); return false; }
/* returns 0 on success, or an errno on failure */ static int readOrWriteBytes (tr_session * session, tr_torrent * tor, int ioMode, tr_file_index_t fileIndex, uint64_t fileOffset, void * buf, size_t buflen) { tr_sys_file_t fd; int err = 0; const bool doWrite = ioMode >= TR_IO_WRITE; const tr_info * const info = &tor->info; const tr_file * const file = &info->files[fileIndex]; assert (fileIndex < info->fileCount); assert (!file->length || (fileOffset < file->length)); assert (fileOffset + buflen <= file->length); if (!file->length) return 0; /*** **** Find the fd ***/ fd = tr_fdFileGetCached (session, tr_torrentId (tor), fileIndex, doWrite); if (fd == TR_BAD_SYS_FILE) { /* it's not cached, so open/create it now */ char * subpath; const char * base; /* see if the file exists... */ if (!tr_torrentFindFile2 (tor, fileIndex, &base, &subpath, NULL)) { /* we can't read a file that doesn't exist... */ if (!doWrite) err = ENOENT; /* figure out where the file should go, so we can create it */ base = tr_torrentGetCurrentDir (tor); subpath = tr_sessionIsIncompleteFileNamingEnabled (tor->session) ? tr_torrentBuildPartial (tor, fileIndex) : tr_strdup (file->name); } if (!err) { /* open (and maybe create) the file */ char * filename = tr_buildPath (base, subpath, NULL); const int prealloc = file->dnd || !doWrite ? TR_PREALLOCATE_NONE : tor->session->preallocationMode; if (((fd = tr_fdFileCheckout (session, tor->uniqueId, fileIndex, filename, doWrite, prealloc, file->length))) == TR_BAD_SYS_FILE) { err = errno; tr_logAddTorErr (tor, "tr_fdFileCheckout failed for \"%s\": %s", filename, tr_strerror (err)); } else if (doWrite) { /* make a note that we just created a file */ tr_statsFileCreated (tor->session); } tr_free (filename); } tr_free (subpath); } /*** **** Use the fd ***/ if (!err) { tr_error * error = NULL; if (ioMode == TR_IO_READ) { if (!tr_sys_file_read_at (fd, buf, buflen, fileOffset, NULL, &error)) { err = error->code; tr_logAddTorErr (tor, "read failed for \"%s\": %s", file->name, error->message); tr_error_free (error); } } else if (ioMode == TR_IO_WRITE) { if (!tr_sys_file_write_at (fd, buf, buflen, fileOffset, NULL, &error)) { err = error->code; tr_logAddTorErr (tor, "write failed for \"%s\": %s", file->name, error->message); tr_error_free (error); } } else if (ioMode == TR_IO_PREFETCH) { tr_sys_file_prefetch (fd, fileOffset, buflen, NULL); } else { abort (); } } return err; }