static uint64_t loadDND (tr_variant * dict, tr_torrent * tor) { uint64_t ret = 0; tr_variant * list = NULL; const tr_file_index_t n = tor->info.fileCount; if (tr_variantDictFindList (dict, TR_KEY_dnd, &list) && (tr_variantListSize (list) == n)) { int64_t tmp; tr_file_index_t * dl = tr_new (tr_file_index_t, n); tr_file_index_t * dnd = tr_new (tr_file_index_t, n); tr_file_index_t i, dlCount = 0, dndCount = 0; for (i=0; i<n; ++i) { if (tr_variantGetInt (tr_variantListChild (list, i), &tmp) && tmp) dnd[dndCount++] = i; else dl[dlCount++] = i; } if (dndCount) { tr_torrentInitFileDLs (tor, dnd, dndCount, false); tr_logAddTorDbg (tor, "Resume file found %d files listed as dnd", dndCount); } if (dlCount) { tr_torrentInitFileDLs (tor, dl, dlCount, true); tr_logAddTorDbg (tor, "Resume file found %d files marked for download", dlCount); } tr_free (dnd); tr_free (dl); ret = TR_FR_DND; } else { tr_logAddTorDbg ( tor, "Couldn't load DND flags. DND list (%p) has %zu children; torrent has %d files", list, tr_variantListSize (list), (int)n); } return ret; }
/** * @brief Announce the given torrent on the local network * * @param[in] t Torrent to announce * @return Returns true on success * * Send a query for torrent t out to the LPD multicast group (or the LAN, for that * matter). A listening client on the same network might react by adding us to his * peer pool for torrent t. */ bool tr_lpdSendAnnounce(const tr_torrent* t) { size_t i; const char fmt[] = "BT-SEARCH * HTTP/1.1\r\n" // BT-SEARCH * HTTP/1.1 "Host: %s:%u\r\n" // Host: 239.192.152.143:6771 "Port: %u\r\n" // Port: <porta do Transmission> "Infohash: %s\r\n" // Infohash: <hash do torrent> "\r\n" "\r\n"; // info_hash do torrent char hashString[lengthof(t->info.hashString)]; // mensagem de announce multicast montada char query[lpd_maxDatagramLength + 1] = { }; (...) /* prepare a zero-terminated announce message */ tr_snprintf(query, lpd_maxDatagramLength + 1, fmt, (...), "239.192.152.143", 6771, lpd_port, hashString); // porta de conexão do Transmission e hash do torrent /* actually send the query out using [lpd_socket2] */ { const int len = strlen(query); /* destination address info has already been set up in tr_lpdInit (), * so we refrain from preparing another sockaddr_in here */ int res = sendto(lpd_socket2, query, len, 0, (const struct sockaddr*) &lpd_mcastAddr, sizeof lpd_mcastAddr); if (res != len) return false; } tr_logAddTorDbg(t, "LPD announce message away"); return true; }
static uint64_t loadPeers(tr_variant* dict, tr_torrent* tor) { uint8_t const* str; size_t len; uint64_t ret = 0; if (tr_variantDictFindRaw(dict, TR_KEY_peers2, &str, &len)) { int const numAdded = addPeers(tor, str, len); tr_logAddTorDbg(tor, "Loaded %d IPv4 peers from resume file", numAdded); ret = TR_FR_PEERS; } if (tr_variantDictFindRaw(dict, TR_KEY_peers2_6, &str, &len)) { int const numAdded = addPeers(tor, str, len); tr_logAddTorDbg(tor, "Loaded %d IPv6 peers from resume file", numAdded); ret = TR_FR_PEERS; } return ret; }
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; }
static uint64_t loadProgress(tr_variant* dict, tr_torrent* tor) { uint64_t ret = 0; tr_variant* prog; tr_info const* inf = tr_torrentInfo(tor); for (size_t i = 0; i < inf->pieceCount; ++i) { inf->pieces[i].timeChecked = 0; } if (tr_variantDictFindDict(dict, TR_KEY_progress, &prog)) { char const* err; char const* str; uint8_t const* raw; size_t rawlen; tr_variant* l; tr_variant* b; struct tr_bitfield blocks = TR_BITFIELD_INIT; if (tr_variantDictFindList(prog, TR_KEY_time_checked, &l)) { /* per-piece timestamps were added in 2.20. If some of a file's pieces have been checked more recently than the file's mtime, and some lest recently, then that file will have a list containing timestamps for each piece. However, the most common use case is that the file doesn't change after it's downloaded. To reduce overhead in the .resume file, only a single timestamp is saved for the file if *all* or *none* of the pieces were tested more recently than the file's mtime. */ for (tr_file_index_t fi = 0; fi < inf->fileCount; ++fi) { tr_variant* b = tr_variantListChild(l, fi); tr_file const* f = &inf->files[fi]; if (tr_variantIsInt(b)) { int64_t t; tr_variantGetInt(b, &t); for (tr_piece_index_t i = f->firstPiece; i <= f->lastPiece; ++i) { inf->pieces[i].timeChecked = (time_t)t; } } else if (tr_variantIsList(b)) { int64_t offset = 0; int const pieces = f->lastPiece + 1 - f->firstPiece; tr_variantGetInt(tr_variantListChild(b, 0), &offset); for (int i = 0; i < pieces; ++i) { int64_t t = 0; tr_variantGetInt(tr_variantListChild(b, i + 1), &t); inf->pieces[f->firstPiece + i].timeChecked = (time_t)(t != 0 ? t + offset : 0); } } } } else if (tr_variantDictFindList(prog, TR_KEY_mtimes, &l)) { /* Before 2.20, we stored the files' mtimes in the .resume file. When loading the .resume file, a torrent's file would be flagged as untested if its stored mtime didn't match its real mtime. */ for (tr_file_index_t fi = 0; fi < inf->fileCount; ++fi) { int64_t t; if (tr_variantGetInt(tr_variantListChild(l, fi), &t)) { tr_file const* f = &inf->files[fi]; time_t const mtime = tr_torrentGetFileMTime(tor, fi); time_t const timeChecked = mtime == t ? mtime : 0; for (tr_piece_index_t i = f->firstPiece; i <= f->lastPiece; ++i) { inf->pieces[i].timeChecked = timeChecked; } } } } err = NULL; tr_bitfieldConstruct(&blocks, tor->blockCount); if ((b = tr_variantDictFind(prog, TR_KEY_blocks)) != NULL) { size_t buflen; uint8_t const* buf; if (!tr_variantGetRaw(b, &buf, &buflen)) { err = "Invalid value for \"blocks\""; } else if (buflen == 3 && memcmp(buf, "all", 3) == 0) { tr_bitfieldSetHasAll(&blocks); } else if (buflen == 4 && memcmp(buf, "none", 4) == 0) { tr_bitfieldSetHasNone(&blocks); } else { tr_bitfieldSetRaw(&blocks, buf, buflen, true); } } else if (tr_variantDictFindStr(prog, TR_KEY_have, &str, NULL)) { if (strcmp(str, "all") == 0) { tr_bitfieldSetHasAll(&blocks); } else { err = "Invalid value for HAVE"; } } else if (tr_variantDictFindRaw(prog, TR_KEY_bitfield, &raw, &rawlen)) { tr_bitfieldSetRaw(&blocks, raw, rawlen, true); } else { err = "Couldn't find 'pieces' or 'have' or 'bitfield'"; } if (err != NULL) { tr_logAddTorDbg(tor, "Torrent needs to be verified - %s", err); } else { tr_cpBlockInit(&tor->completion, &blocks); } tr_bitfieldDestruct(&blocks); ret = TR_FR_PROGRESS; } return ret; }
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 bool verifyTorrent (tr_torrent * tor, bool * stopFlag) { time_t end; SHA_CTX sha; int fd = -1; int64_t filePos = 0; bool changed = 0; bool hadPiece = 0; 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; const time_t begin = tr_time (); const size_t buflen = 1024 * 128; /* 128 KiB buffer */ uint8_t * buffer = tr_valloc (buflen); SHA1_Init (&sha); tr_logAddTorDbg (tor, "%s", "verifying torrent..."); tr_torrentSetChecked (tor, 0); while (!*stopFlag && (pieceIndex < tor->info.pieceCount)) { uint32_t leftInPiece; uint32_t bytesThisPass; uint64_t leftInFile; const tr_file * file = &tor->info.files[fileIndex]; /* if we're starting a new piece... */ if (piecePos == 0) hadPiece = tr_cpPieceIsComplete (&tor->completion, pieceIndex); /* if we're starting a new file... */ if (!filePos && (fd<0) && (fileIndex!=prevFileIndex)) { char * filename = tr_torrentFindFile (tor, fileIndex); fd = filename == NULL ? -1 : tr_open_file_for_scanning (filename); 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 >= 0) { const ssize_t numRead = tr_pread (fd, buffer, bytesThisPass, filePos); if (numRead > 0) { bytesThisPass = (uint32_t)numRead; SHA1_Update (&sha, buffer, bytesThisPass); #if defined HAVE_POSIX_FADVISE && defined POSIX_FADV_DONTNEED 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]; SHA1_Final (hash, &sha); hasPiece = !memcmp (hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH); 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); } SHA1_Init (&sha); pieceIndex++; piecePos = 0; } /* if we're finishing a file... */ if (leftInFile == 0) { if (fd >= 0) { tr_close_file (fd); fd = -1; } fileIndex++; filePos = 0; } } /* cleanup */ if (fd >= 0) tr_close_file (fd); 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; }