static void blocklistLoad(tr_blocklistFile* b) { tr_sys_file_t fd; uint64_t byteCount; tr_sys_path_info info; char* base; tr_error* error = NULL; char const* err_fmt = _("Couldn't read \"%1$s\": %2$s"); blocklistClose(b); if (!tr_sys_path_get_info(b->filename, 0, &info, NULL)) { return; } byteCount = info.size; if (byteCount == 0) { return; } fd = tr_sys_file_open(b->filename, TR_SYS_FILE_READ, 0, &error); if (fd == TR_BAD_SYS_FILE) { tr_logAddError(err_fmt, b->filename, error->message); tr_error_free(error); return; } b->rules = tr_sys_file_map_for_reading(fd, 0, byteCount, &error); if (b->rules == NULL) { tr_logAddError(err_fmt, b->filename, error->message); tr_sys_file_close(fd, NULL); tr_error_free(error); return; } b->fd = fd; b->byteCount = byteCount; b->ruleCount = byteCount / sizeof(struct tr_ipv4_range); base = tr_sys_path_basename(b->filename, NULL); tr_logAddInfo(_("Blocklist \"%s\" contains %zu entries"), base, b->ruleCount); tr_free(base); }
static int test_error_propagate (void) { tr_error * err = NULL; tr_error * err2 = NULL; tr_error_set_literal (&err, 1, "oops"); check (err != NULL); check_int_eq (1, err->code); check_streq ("oops", err->message); tr_error_propagate (&err2, &err); check (err2 != NULL); check_int_eq (1, err2->code); check_streq ("oops", err2->message); check (err == NULL); tr_error_propagate_prefixed (&err, &err2, "error: "); check (err != NULL); check_int_eq (1, err->code); check_streq ("error: oops", err->message); check (err2 == NULL); tr_error_propagate (NULL, &err); check (err == NULL); tr_error_free (err2); return 0; }
static int test_error_set (void) { tr_error * err = NULL; tr_error_prefix (&err, "error: "); check (err == NULL); tr_error_set (&err, 1, "error: %s (%d)", "oops", 2); check (err != NULL); check_int_eq (1, err->code); check_streq ("error: oops (2)", err->message); tr_error_clear (&err); check (err == NULL); tr_error_set_literal (&err, 2, "oops"); check (err != NULL); check_int_eq (2, err->code); check_streq ("oops", err->message); tr_error_prefix (&err, "error: "); check (err != NULL); check_int_eq (2, err->code); check_streq ("error: oops", err->message); tr_error_free (err); return 0; }
static tr_watchdir_status onFileAdded(tr_watchdir_t dir, char const* name, void* context) { tr_session* session = context; if (!tr_str_has_suffix(name, ".torrent")) { return TR_WATCHDIR_IGNORE; } char* filename = tr_buildPath(tr_watchdir_get_path(dir), name, NULL); tr_ctor* ctor = tr_ctorNew(session); int err = tr_ctorSetMetainfoFromFile(ctor, filename); if (err == 0) { tr_torrentNew(ctor, &err, NULL); if (err == TR_PARSE_ERR) { tr_logAddError("Error parsing .torrent file \"%s\"", name); } else { bool trash = false; bool const test = tr_ctorGetDeleteSource(ctor, &trash); tr_logAddInfo("Parsing .torrent file successful \"%s\"", name); if (test && trash) { tr_error* error = NULL; tr_logAddInfo("Deleting input .torrent file \"%s\"", name); if (!tr_sys_path_remove(filename, &error)) { tr_logAddError("Error deleting .torrent file: %s", error->message); tr_error_free(error); } } else { char* new_filename = tr_strdup_printf("%s.added", filename); tr_sys_path_rename(filename, new_filename, NULL); tr_free(new_filename); } } } else { err = TR_PARSE_ERR; } tr_ctorFree(ctor); tr_free(filename); return err == TR_PARSE_ERR ? TR_WATCHDIR_RETRY : TR_WATCHDIR_ACCEPT; }
static char* tr_getcwd (void) { char * result; tr_error * error = NULL; result = tr_sys_dir_get_current (&error); if (result == NULL) { fprintf (stderr, "getcwd error: \"%s\"", error->message); tr_error_free (error); result = tr_strdup (""); } return result; }
static bool reopen_log_file (const char *filename) { tr_error * error = NULL; const tr_sys_file_t old_log_file = logfile; const tr_sys_file_t new_log_file = tr_sys_file_open (filename, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE | TR_SYS_FILE_APPEND, 0666, &error); if (new_log_file == TR_BAD_SYS_FILE) { fprintf (stderr, "Couldn't (re)open log file \"%s\": %s\n", filename, error->message); tr_error_free (error); return false; } logfile = new_log_file; if (old_log_file != TR_BAD_SYS_FILE) tr_sys_file_close (old_log_file, NULL); return true; }
int tr_blocklistFileSetContent (tr_blocklistFile * b, const char * filename) { tr_sys_file_t in; tr_sys_file_t out; int inCount = 0; char line[2048]; const char * err_fmt = _("Couldn't read \"%1$s\": %2$s"); struct tr_ipv4_range * ranges = NULL; size_t ranges_alloc = 0; size_t ranges_count = 0; tr_error * error = NULL; if (!filename) { blocklistDelete (b); return 0; } in = tr_sys_file_open (filename, TR_SYS_FILE_READ, 0, &error); if (in == TR_BAD_SYS_FILE) { tr_logAddError (err_fmt, filename, error->message); tr_error_free (error); return 0; } blocklistClose (b); out = tr_sys_file_open (b->filename, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE | TR_SYS_FILE_TRUNCATE, 0666, &error); if (out == TR_BAD_SYS_FILE) { tr_logAddError (err_fmt, b->filename, error->message); tr_error_free (error); tr_sys_file_close (in, NULL); return 0; } /* load the rules into memory */ while (tr_sys_file_read_line (in, line, sizeof (line), NULL)) { struct tr_ipv4_range range; ++inCount; if (!parseLine (line, &range)) { /* don't try to display the actual lines - it causes issues */ tr_logAddError (_("blocklist skipped invalid address at line %d"), inCount); continue; } if (ranges_alloc == ranges_count) { ranges_alloc += 4096; /* arbitrary */ ranges = tr_renew (struct tr_ipv4_range, ranges, ranges_alloc); } ranges[ranges_count++] = range; }
static uint64_t loadFromFile(tr_torrent* tor, uint64_t fieldsToLoad) { TR_ASSERT(tr_isTorrent(tor)); size_t len; int64_t i; char const* str; char* filename; tr_variant top; bool boolVal; uint64_t fieldsLoaded = 0; bool const wasDirty = tor->isDirty; tr_error* error = NULL; filename = getResumeFilename(tor); if (!tr_variantFromFile(&top, TR_VARIANT_FMT_BENC, filename, &error)) { tr_logAddTorDbg(tor, "Couldn't read \"%s\": %s", filename, error->message); tr_error_free(error); tr_free(filename); return fieldsLoaded; } tr_logAddTorDbg(tor, "Read resume file \"%s\"", filename); if ((fieldsToLoad & TR_FR_CORRUPT) != 0 && tr_variantDictFindInt(&top, TR_KEY_corrupt, &i)) { tor->corruptPrev = i; fieldsLoaded |= TR_FR_CORRUPT; } if ((fieldsToLoad & (TR_FR_PROGRESS | TR_FR_DOWNLOAD_DIR)) != 0 && tr_variantDictFindStr(&top, TR_KEY_destination, &str, &len) && str != NULL && *str != '\0') { bool const is_current_dir = tor->currentDir == tor->downloadDir; tr_free(tor->downloadDir); tor->downloadDir = tr_strndup(str, len); if (is_current_dir) { tor->currentDir = tor->downloadDir; } fieldsLoaded |= TR_FR_DOWNLOAD_DIR; } if ((fieldsToLoad & (TR_FR_PROGRESS | TR_FR_INCOMPLETE_DIR)) != 0 && tr_variantDictFindStr(&top, TR_KEY_incomplete_dir, &str, &len) && str != NULL && *str != '\0') { bool const is_current_dir = tor->currentDir == tor->incompleteDir; tr_free(tor->incompleteDir); tor->incompleteDir = tr_strndup(str, len); if (is_current_dir) { tor->currentDir = tor->incompleteDir; } fieldsLoaded |= TR_FR_INCOMPLETE_DIR; } if ((fieldsToLoad & TR_FR_DOWNLOADED) != 0 && tr_variantDictFindInt(&top, TR_KEY_downloaded, &i)) { tor->downloadedPrev = i; fieldsLoaded |= TR_FR_DOWNLOADED; } if ((fieldsToLoad & TR_FR_UPLOADED) != 0 && tr_variantDictFindInt(&top, TR_KEY_uploaded, &i)) { tor->uploadedPrev = i; fieldsLoaded |= TR_FR_UPLOADED; } if ((fieldsToLoad & TR_FR_MAX_PEERS) != 0 && tr_variantDictFindInt(&top, TR_KEY_max_peers, &i)) { tor->maxConnectedPeers = i; fieldsLoaded |= TR_FR_MAX_PEERS; } if ((fieldsToLoad & TR_FR_RUN) != 0 && tr_variantDictFindBool(&top, TR_KEY_paused, &boolVal)) { tor->isRunning = !boolVal; fieldsLoaded |= TR_FR_RUN; } if ((fieldsToLoad & TR_FR_ADDED_DATE) != 0 && tr_variantDictFindInt(&top, TR_KEY_added_date, &i)) { tor->addedDate = i; fieldsLoaded |= TR_FR_ADDED_DATE; } if ((fieldsToLoad & TR_FR_DONE_DATE) != 0 && tr_variantDictFindInt(&top, TR_KEY_done_date, &i)) { tor->doneDate = i; fieldsLoaded |= TR_FR_DONE_DATE; } if ((fieldsToLoad & TR_FR_ACTIVITY_DATE) != 0 && tr_variantDictFindInt(&top, TR_KEY_activity_date, &i)) { tr_torrentSetActivityDate(tor, i); fieldsLoaded |= TR_FR_ACTIVITY_DATE; } if ((fieldsToLoad & TR_FR_TIME_SEEDING) != 0 && tr_variantDictFindInt(&top, TR_KEY_seeding_time_seconds, &i)) { tor->secondsSeeding = i; fieldsLoaded |= TR_FR_TIME_SEEDING; } if ((fieldsToLoad & TR_FR_TIME_DOWNLOADING) != 0 && tr_variantDictFindInt(&top, TR_KEY_downloading_time_seconds, &i)) { tor->secondsDownloading = i; fieldsLoaded |= TR_FR_TIME_DOWNLOADING; } if ((fieldsToLoad & TR_FR_BANDWIDTH_PRIORITY) != 0 && tr_variantDictFindInt(&top, TR_KEY_bandwidth_priority, &i) && tr_isPriority(i)) { tr_torrentSetPriority(tor, i); fieldsLoaded |= TR_FR_BANDWIDTH_PRIORITY; } if ((fieldsToLoad & TR_FR_PEERS) != 0) { fieldsLoaded |= loadPeers(&top, tor); } if ((fieldsToLoad & TR_FR_FILE_PRIORITIES) != 0) { fieldsLoaded |= loadFilePriorities(&top, tor); } if ((fieldsToLoad & TR_FR_PROGRESS) != 0) { fieldsLoaded |= loadProgress(&top, tor); } if ((fieldsToLoad & TR_FR_DND) != 0) { fieldsLoaded |= loadDND(&top, tor); } if ((fieldsToLoad & TR_FR_SPEEDLIMIT) != 0) { fieldsLoaded |= loadSpeedLimits(&top, tor); } if ((fieldsToLoad & TR_FR_RATIOLIMIT) != 0) { fieldsLoaded |= loadRatioLimits(&top, tor); } if ((fieldsToLoad & TR_FR_IDLELIMIT) != 0) { fieldsLoaded |= loadIdleLimits(&top, tor); } if ((fieldsToLoad & TR_FR_FILENAMES) != 0) { fieldsLoaded |= loadFilenames(&top, tor); } if ((fieldsToLoad & TR_FR_NAME) != 0) { fieldsLoaded |= loadName(&top, tor); } /* loading the resume file triggers of a lot of changes, * but none of them needs to trigger a re-saving of the * same resume information... */ tor->isDirty = wasDirty; tr_variantFree(&top); tr_free(filename); return fieldsLoaded; }
/** * 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; } }
/* 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; }
int main (int argc, char * argv[]) { int i; int changedCount = 0; #ifdef _WIN32 tr_win32_make_args_utf8 (&argc, &argv); #endif files = tr_new0 (const char*, argc); tr_logSetLevel (TR_LOG_ERROR); if (parseCommandLine (argc, (const char**)argv)) return EXIT_FAILURE; if (showVersion) { fprintf (stderr, MY_NAME" "LONG_VERSION_STRING"\n"); return EXIT_SUCCESS; } if (fileCount < 1) { fprintf (stderr, "ERROR: No torrent files specified.\n"); tr_getopt_usage (MY_NAME, getUsage (), options); fprintf (stderr, "\n"); return EXIT_FAILURE; } if (!add && !deleteme && !replace[0]) { fprintf (stderr, "ERROR: Must specify -a, -d or -r\n"); tr_getopt_usage (MY_NAME, getUsage (), options); fprintf (stderr, "\n"); return EXIT_FAILURE; } for (i=0; i<fileCount; ++i) { tr_variant top; bool changed = false; const char * filename = files[i]; tr_error * error = NULL; printf ("%s\n", filename); if (!tr_variantFromFile (&top, TR_VARIANT_FMT_BENC, filename, &error)) { printf ("\tError reading file: %s\n", error->message); tr_error_free (error); continue; } if (deleteme != NULL) changed |= removeURL (&top, deleteme); if (add != NULL) changed = addURL (&top, add); if (replace[0] && replace[1]) changed |= replaceURL (&top, replace[0], replace[1]); if (changed) { ++changedCount; tr_variantToFile (&top, TR_VARIANT_FMT_BENC, filename); } tr_variantFree (&top); } printf ("Changed %d files\n", changedCount); tr_free (files); return EXIT_SUCCESS; }