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); }
void libttest_zero_torrent_populate (tr_torrent * tor, bool complete) { tr_file_index_t i; for (i=0; i<tor->info.fileCount; ++i) { int err; uint64_t j; tr_sys_file_t fd; char * path; char * dirname; const tr_file * file = &tor->info.files[i]; if (!complete && (i==0)) path = tr_strdup_printf ("%s%c%s.part", tor->currentDir, TR_PATH_DELIMITER, file->name); else path = tr_strdup_printf ("%s%c%s", tor->currentDir, TR_PATH_DELIMITER, file->name); dirname = tr_sys_path_dirname (path, NULL); tr_sys_dir_create (dirname, TR_SYS_DIR_CREATE_PARENTS, 0700, NULL); fd = tr_sys_file_open (path, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE | TR_SYS_FILE_TRUNCATE, 0600, NULL); for (j=0; j<file->length; ++j) tr_sys_file_write (fd, ((!complete) && (i==0) && (j<tor->info.pieceSize)) ? "\1" : "\0", 1, NULL, NULL); tr_sys_file_close (fd, NULL); tr_free (dirname); tr_free (path); path = tr_torrentFindFile (tor, i); assert (path != NULL); err = errno; assert (tr_sys_path_exists (path, NULL)); errno = err; tr_free (path); } libttest_sync (); libttest_blockingTorrentVerify (tor); if (complete) assert (tr_torrentStat(tor)->leftUntilDone == 0); else assert (tr_torrentStat(tor)->leftUntilDone == tor->info.pieceSize); }
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 bool verifyTorrent(tr_torrent* tor, bool* stopFlag) { time_t end; tr_sha1_ctx_t sha; tr_sys_file_t fd = TR_BAD_SYS_FILE; uint64_t filePos = 0; bool changed = false; bool hadPiece = false; time_t lastSleptAt = 0; uint32_t piecePos = 0; tr_file_index_t fileIndex = 0; tr_file_index_t prevFileIndex = !fileIndex; tr_piece_index_t pieceIndex = 0; time_t const begin = tr_time(); size_t const buflen = 1024 * 128; /* 128 KiB buffer */ uint8_t* buffer = tr_valloc(buflen); sha = tr_sha1_init(); tr_logAddTorDbg(tor, "%s", "verifying torrent..."); tr_torrentSetChecked(tor, 0); while (!*stopFlag && pieceIndex < tor->info.pieceCount) { uint64_t leftInPiece; uint64_t bytesThisPass; uint64_t leftInFile; tr_file const* file = &tor->info.files[fileIndex]; /* if we're starting a new piece... */ if (piecePos == 0) { hadPiece = tr_torrentPieceIsComplete(tor, pieceIndex); } /* if we're starting a new file... */ if (filePos == 0 && fd == TR_BAD_SYS_FILE && fileIndex != prevFileIndex) { char* filename = tr_torrentFindFile(tor, fileIndex); fd = filename == NULL ? TR_BAD_SYS_FILE : tr_sys_file_open(filename, TR_SYS_FILE_READ | TR_SYS_FILE_SEQUENTIAL, 0, NULL); tr_free(filename); prevFileIndex = fileIndex; } /* figure out how much we can read this pass */ leftInPiece = tr_torPieceCountBytes(tor, pieceIndex) - piecePos; leftInFile = file->length - filePos; bytesThisPass = MIN(leftInFile, leftInPiece); bytesThisPass = MIN(bytesThisPass, buflen); /* read a bit */ if (fd != TR_BAD_SYS_FILE) { uint64_t numRead; if (tr_sys_file_read_at(fd, buffer, bytesThisPass, filePos, &numRead, NULL) && numRead > 0) { bytesThisPass = numRead; tr_sha1_update(sha, buffer, bytesThisPass); #if defined HAVE_POSIX_FADVISE && defined POSIX_FADV_DONTNEED (void)posix_fadvise(fd, filePos, bytesThisPass, POSIX_FADV_DONTNEED); #endif } } /* move our offsets */ leftInPiece -= bytesThisPass; leftInFile -= bytesThisPass; piecePos += bytesThisPass; filePos += bytesThisPass; /* if we're finishing a piece... */ if (leftInPiece == 0) { time_t now; bool hasPiece; uint8_t hash[SHA_DIGEST_LENGTH]; tr_sha1_final(sha, hash); hasPiece = memcmp(hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH) == 0; if (hasPiece || hadPiece) { tr_torrentSetHasPiece(tor, pieceIndex, hasPiece); changed |= hasPiece != hadPiece; } tr_torrentSetPieceChecked(tor, pieceIndex); now = tr_time(); tor->anyDate = now; /* sleeping even just a few msec per second goes a long * way towards reducing IO load... */ if (lastSleptAt != now) { lastSleptAt = now; tr_wait_msec(MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY); } sha = tr_sha1_init(); pieceIndex++; piecePos = 0; } /* if we're finishing a file... */ if (leftInFile == 0) { if (fd != TR_BAD_SYS_FILE) { tr_sys_file_close(fd, NULL); fd = TR_BAD_SYS_FILE; } fileIndex++; filePos = 0; } } /* cleanup */ if (fd != TR_BAD_SYS_FILE) { tr_sys_file_close(fd, NULL); } tr_sha1_final(sha, NULL); free(buffer); /* stopwatch */ end = tr_time(); tr_logAddTorDbg(tor, "Verification is done. It took %d seconds to verify %" PRIu64 " bytes (%" PRIu64 " bytes per second)", (int)(end - begin), tor->info.totalSize, (uint64_t)(tor->info.totalSize / (1 + (end - begin)))); return changed; }
static void dht_bootstrap (void *closure) { struct bootstrap_closure *cl = closure; int i; int num = cl->len / 6, num6 = cl->len6 / 18; if (session != cl->session) return; if (cl->len > 0) tr_logAddNamedInfo ("DHT", "Bootstrapping from %d IPv4 nodes", num); if (cl->len6 > 0) tr_logAddNamedInfo ("DHT", "Bootstrapping from %d IPv6 nodes", num6); for (i = 0; i < MAX (num, num6); i++) { if (i < num && !bootstrap_done (cl->session, AF_INET)) { tr_port port; struct tr_address addr; memset (&addr, 0, sizeof (addr)); addr.type = TR_AF_INET; memcpy (&addr.addr.addr4, &cl->nodes[i * 6], 4); memcpy (&port, &cl->nodes[i * 6 + 4], 2); port = ntohs (port); tr_dhtAddNode (cl->session, &addr, port, 1); } if (i < num6 && !bootstrap_done (cl->session, AF_INET6)) { tr_port port; struct tr_address addr; memset (&addr, 0, sizeof (addr)); addr.type = TR_AF_INET6; memcpy (&addr.addr.addr6, &cl->nodes6[i * 18], 16); memcpy (&port, &cl->nodes6[i * 18 + 16], 2); port = ntohs (port); tr_dhtAddNode (cl->session, &addr, port, 1); } /* Our DHT code is able to take up to 9 nodes in a row without dropping any. After that, it takes some time to split buckets. So ping the first 8 nodes quickly, then slow down. */ if (i < 8) nap (2); else nap (15); if (bootstrap_done (session, 0)) break; } if (!bootstrap_done (cl->session, 0)) { char *bootstrap_file; tr_sys_file_t f = TR_BAD_SYS_FILE; bootstrap_file = tr_buildPath (cl->session->configDir, "dht.bootstrap", NULL); if (bootstrap_file) f = tr_sys_file_open (bootstrap_file, TR_SYS_FILE_READ, 0, NULL); if (f != TR_BAD_SYS_FILE) { tr_logAddNamedInfo ("DHT", "Attempting manual bootstrap"); for (;;) { char buf[201]; char *p; int port = 0; if (!tr_sys_file_read_line (f, buf, 200, NULL)) break; p = memchr (buf, ' ', strlen (buf)); if (p != NULL) port = atoi (p + 1); if (p == NULL || port <= 0 || port >= 0x10000) { tr_logAddNamedError ("DHT", "Couldn't parse %s", buf); continue; } *p = '\0'; bootstrap_from_name (buf, port, bootstrap_af (session)); if (bootstrap_done (cl->session, 0)) break; } tr_sys_file_close (f, NULL); } tr_free (bootstrap_file); } if (!bootstrap_done (cl->session, 0)) { for (i = 0; i < 6; i++) { /* We don't want to abuse our bootstrap nodes, so be very slow. The initial wait is to give other nodes a chance to contact us before we attempt to contact a bootstrap node, for example because we've just been restarted. */ nap (40); if (bootstrap_done (cl->session, 0)) break; if (i == 0) tr_logAddNamedInfo ("DHT", "Attempting bootstrap from dht.transmissionbt.com"); bootstrap_from_name ("dht.transmissionbt.com", 6881, bootstrap_af (session)); } } if (cl->nodes) tr_free (cl->nodes); if (cl->nodes6) tr_free (cl->nodes6); tr_free (closure); tr_logAddNamedDbg ("DHT", "Finished bootstrapping"); }
/** * 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; } }
static int test_get_info (void) { char * const test_dir = create_test_dir (__FUNCTION__); tr_sys_path_info info; tr_sys_file_t fd; tr_error * err = NULL; char * path1, * path2; time_t t; path1 = tr_buildPath (test_dir, "a", NULL); path2 = tr_buildPath (test_dir, "b", NULL); /* Can't get info of non-existent file/directory */ check (!tr_sys_path_get_info (path1, 0, &info, &err)); check (err != NULL); tr_error_clear (&err); t = time (NULL); libtest_create_file_with_string_contents (path1, "test"); /* Good file info */ clear_path_info (&info); check (tr_sys_path_get_info (path1, 0, &info, &err)); check (err == NULL); check_int_eq (TR_SYS_PATH_IS_FILE, info.type); check_int_eq (4, info.size); check (info.last_modified_at >= t && info.last_modified_at <= time (NULL)); /* Good file info (by handle) */ fd = tr_sys_file_open (path1, TR_SYS_FILE_READ, 0, NULL); clear_path_info (&info); check (tr_sys_file_get_info (fd, &info, &err)); check (err == NULL); check_int_eq (TR_SYS_PATH_IS_FILE, info.type); check_int_eq (4, info.size); check (info.last_modified_at >= t && info.last_modified_at <= time (NULL)); tr_sys_file_close (fd, NULL); tr_sys_path_remove (path1, NULL); /* Good directory info */ t = time (NULL); tr_sys_dir_create (path1, 0, 0777, NULL); clear_path_info (&info); check (tr_sys_path_get_info (path1, 0, &info, &err)); check (err == NULL); check_int_eq (TR_SYS_PATH_IS_DIRECTORY, info.type); check (info.size != (uint64_t) -1); check (info.last_modified_at >= t && info.last_modified_at <= time (NULL)); tr_sys_path_remove (path1, NULL); if (create_symlink (path1, path2, false)) { /* Can't get info of non-existent file/directory */ check (!tr_sys_path_get_info (path1, 0, &info, &err)); check (err != NULL); tr_error_clear (&err); t = time (NULL); libtest_create_file_with_string_contents (path2, "test"); /* Good file info */ clear_path_info (&info); check (tr_sys_path_get_info (path1, 0, &info, &err)); check (err == NULL); check_int_eq (TR_SYS_PATH_IS_FILE, info.type); check_int_eq (4, info.size); check (info.last_modified_at >= t && info.last_modified_at <= time (NULL)); /* Good file info (by handle) */ fd = tr_sys_file_open (path1, TR_SYS_FILE_READ, 0, NULL); clear_path_info (&info); check (tr_sys_file_get_info (fd, &info, &err)); check (err == NULL); check_int_eq (TR_SYS_PATH_IS_FILE, info.type); check_int_eq (4, info.size); check (info.last_modified_at >= t && info.last_modified_at <= time (NULL)); tr_sys_file_close (fd, NULL); tr_sys_path_remove (path2, NULL); /* Good directory info */ t = time (NULL); tr_sys_dir_create (path2, 0, 0777, NULL); clear_path_info (&info); check (tr_sys_path_get_info (path1, 0, &info, &err)); check (err == NULL); check_int_eq (TR_SYS_PATH_IS_DIRECTORY, info.type); check (info.size != (uint64_t) -1); check (info.last_modified_at >= t && info.last_modified_at <= time (NULL)); tr_sys_path_remove (path2, NULL); tr_sys_path_remove (path1, NULL); } else { fprintf (stderr, "WARNING: [%s] unable to run symlink tests\n", __FUNCTION__); } tr_free (path2); tr_free (path1); tr_free (test_dir); return 0; }