2022-01-20 18:27:56 +00:00
|
|
|
// This file Copyright © 2007-2022 Mnemosyne LLC.
|
|
|
|
// It may be used under GPLv2 (SPDX: GPL-2.0), GPLv3 (SPDX: GPL-3.0),
|
|
|
|
// or any future license endorsed by Mnemosyne LLC.
|
|
|
|
// License text can be found in the licenses/ folder.
|
2008-02-15 16:00:46 +00:00
|
|
|
|
2021-09-19 20:41:35 +00:00
|
|
|
#include <algorithm>
|
2021-12-15 21:25:42 +00:00
|
|
|
#include <ctime>
|
2021-11-20 21:20:45 +00:00
|
|
|
#include <mutex>
|
2022-01-31 19:34:04 +00:00
|
|
|
#include <optional>
|
2021-09-27 22:07:58 +00:00
|
|
|
#include <set>
|
2022-01-31 19:34:04 +00:00
|
|
|
#include <thread>
|
2021-12-29 19:20:09 +00:00
|
|
|
#include <vector>
|
2011-03-16 18:04:23 +00:00
|
|
|
|
2008-02-15 16:00:46 +00:00
|
|
|
#include "transmission.h"
|
|
|
|
#include "completion.h"
|
2014-12-04 12:13:59 +00:00
|
|
|
#include "crypto-utils.h"
|
2014-07-28 04:13:38 +00:00
|
|
|
#include "file.h"
|
2013-01-25 23:34:20 +00:00
|
|
|
#include "log.h"
|
2008-02-15 16:00:46 +00:00
|
|
|
#include "torrent.h"
|
2017-06-08 07:24:12 +00:00
|
|
|
#include "tr-assert.h"
|
2020-11-15 21:53:42 +00:00
|
|
|
#include "utils.h" /* tr_malloc(), tr_free() */
|
2008-02-15 16:00:46 +00:00
|
|
|
#include "verify.h"
|
|
|
|
|
2009-04-06 04:02:51 +00:00
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
2021-10-21 00:39:05 +00:00
|
|
|
static auto constexpr MsecToSleepPerSecondDuringVerify = int{ 100 };
|
2009-09-06 14:05:06 +00:00
|
|
|
|
2022-01-24 19:07:55 +00:00
|
|
|
static bool verifyTorrent(tr_torrent* tor, bool const* stopFlag)
|
2009-04-06 04:02:51 +00:00
|
|
|
{
|
2021-12-29 19:20:09 +00:00
|
|
|
auto const begin = tr_time();
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
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;
|
2022-02-07 04:28:36 +00:00
|
|
|
tr_file_index_t prevFileIndex = ~0;
|
2021-11-25 18:26:51 +00:00
|
|
|
tr_piece_index_t piece = 0;
|
2021-12-29 19:20:09 +00:00
|
|
|
auto buffer = std::vector<std::byte>(1024 * 256);
|
|
|
|
auto sha = tr_sha1_init();
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
tr_logAddTorDbg(tor, "%s", "verifying torrent...");
|
2021-10-29 18:24:30 +00:00
|
|
|
tor->verify_progress = 0;
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-12-15 15:53:20 +00:00
|
|
|
while (!*stopFlag && piece < tor->pieceCount())
|
2009-04-06 04:02:51 +00:00
|
|
|
{
|
2022-01-02 01:25:25 +00:00
|
|
|
auto const file_length = tor->fileSize(fileIndex);
|
2009-04-06 04:02:51 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* if we're starting a new piece... */
|
|
|
|
if (piecePos == 0)
|
|
|
|
{
|
2021-11-25 18:26:51 +00:00
|
|
|
hadPiece = tor->hasPiece(piece);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2009-04-06 04:02:51 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* if we're starting a new file... */
|
|
|
|
if (filePos == 0 && fd == TR_BAD_SYS_FILE && fileIndex != prevFileIndex)
|
2009-04-06 04:02:51 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
char* filename = tr_torrentFindFile(tor, fileIndex);
|
2021-09-15 00:18:09 +00:00
|
|
|
fd = filename == nullptr ? TR_BAD_SYS_FILE :
|
|
|
|
tr_sys_file_open(filename, TR_SYS_FILE_READ | TR_SYS_FILE_SEQUENTIAL, 0, nullptr);
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_free(filename);
|
|
|
|
prevFileIndex = fileIndex;
|
2009-04-06 04:02:51 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* figure out how much we can read this pass */
|
2021-11-26 19:33:56 +00:00
|
|
|
uint64_t leftInPiece = tor->pieceSize(piece) - piecePos;
|
2021-11-28 03:17:47 +00:00
|
|
|
uint64_t leftInFile = file_length - filePos;
|
2021-10-19 16:09:38 +00:00
|
|
|
uint64_t bytesThisPass = std::min(leftInFile, leftInPiece);
|
2021-12-29 19:20:09 +00:00
|
|
|
bytesThisPass = std::min(bytesThisPass, uint64_t(std::size(buffer)));
|
2012-12-05 17:29:46 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* read a bit */
|
|
|
|
if (fd != TR_BAD_SYS_FILE)
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2021-10-19 16:09:38 +00:00
|
|
|
auto numRead = uint64_t{};
|
2021-12-29 19:20:09 +00:00
|
|
|
if (tr_sys_file_read_at(fd, std::data(buffer), bytesThisPass, filePos, &numRead, nullptr) && numRead > 0)
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
bytesThisPass = numRead;
|
2021-12-29 19:20:09 +00:00
|
|
|
tr_sha1_update(sha, std::data(buffer), bytesThisPass);
|
2021-09-15 00:18:09 +00:00
|
|
|
tr_sys_file_advise(fd, filePos, bytesThisPass, TR_SYS_FILE_ADVICE_DONT_NEED, nullptr);
|
2010-10-19 13:56:58 +00:00
|
|
|
}
|
2009-04-06 04:02:51 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* move our offsets */
|
|
|
|
leftInPiece -= bytesThisPass;
|
|
|
|
leftInFile -= bytesThisPass;
|
|
|
|
piecePos += bytesThisPass;
|
|
|
|
filePos += bytesThisPass;
|
2009-04-06 04:02:51 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* if we're finishing a piece... */
|
|
|
|
if (leftInPiece == 0)
|
2009-04-06 04:02:51 +00:00
|
|
|
{
|
2021-11-04 00:55:04 +00:00
|
|
|
auto hash = tr_sha1_final(sha);
|
2021-11-25 18:26:51 +00:00
|
|
|
auto const hasPiece = hash && *hash == tor->pieceHash(piece);
|
2010-12-09 20:43:23 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (hasPiece || hadPiece)
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2021-11-25 18:26:51 +00:00
|
|
|
tor->setHasPiece(piece, hasPiece);
|
2017-04-19 12:04:45 +00:00
|
|
|
changed |= hasPiece != hadPiece;
|
2009-04-06 04:02:51 +00:00
|
|
|
}
|
2012-12-05 17:29:46 +00:00
|
|
|
|
2021-12-15 21:25:42 +00:00
|
|
|
tor->markChanged();
|
2012-12-05 17:29:46 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* sleeping even just a few msec per second goes a long
|
|
|
|
* way towards reducing IO load... */
|
2021-12-16 22:58:58 +00:00
|
|
|
if (auto const now = tr_time(); lastSleptAt != now)
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
lastSleptAt = now;
|
2021-10-21 00:39:05 +00:00
|
|
|
tr_wait_msec(MsecToSleepPerSecondDuringVerify);
|
2009-09-06 14:05:06 +00:00
|
|
|
}
|
2009-08-14 20:55:22 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
sha = tr_sha1_init();
|
2021-11-25 18:26:51 +00:00
|
|
|
++piece;
|
2021-12-15 15:53:20 +00:00
|
|
|
tor->verify_progress = piece / double(tor->pieceCount());
|
2017-04-19 12:04:45 +00:00
|
|
|
piecePos = 0;
|
2009-04-06 04:02:51 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* if we're finishing a file... */
|
|
|
|
if (leftInFile == 0)
|
2009-04-06 04:02:51 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
if (fd != TR_BAD_SYS_FILE)
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2021-09-15 00:18:09 +00:00
|
|
|
tr_sys_file_close(fd, nullptr);
|
2017-04-19 12:04:45 +00:00
|
|
|
fd = TR_BAD_SYS_FILE;
|
2012-12-05 17:29:46 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
fileIndex++;
|
|
|
|
filePos = 0;
|
2009-04-06 04:02:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* cleanup */
|
|
|
|
if (fd != TR_BAD_SYS_FILE)
|
|
|
|
{
|
2021-09-15 00:18:09 +00:00
|
|
|
tr_sys_file_close(fd, nullptr);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
2021-10-29 18:24:30 +00:00
|
|
|
tor->verify_progress.reset();
|
2021-11-04 00:55:04 +00:00
|
|
|
tr_sha1_final(sha);
|
2009-04-06 04:02:51 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* stopwatch */
|
2020-11-15 21:53:42 +00:00
|
|
|
time_t const end = tr_time();
|
2021-08-15 09:41:48 +00:00
|
|
|
tr_logAddTorDbg(
|
|
|
|
tor,
|
|
|
|
"Verification is done. It took %d seconds to verify %" PRIu64 " bytes (%" PRIu64 " bytes per second)",
|
|
|
|
(int)(end - begin),
|
2021-12-15 15:53:20 +00:00
|
|
|
tor->totalSize(),
|
|
|
|
(uint64_t)(tor->totalSize() / (1 + (end - begin))));
|
2009-04-06 04:02:51 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return changed;
|
2009-04-06 04:02:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
2008-02-15 16:00:46 +00:00
|
|
|
|
|
|
|
struct verify_node
|
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_torrent* torrent;
|
|
|
|
tr_verify_done_func callback_func;
|
|
|
|
void* callback_data;
|
|
|
|
uint64_t current_size;
|
2021-09-27 22:07:58 +00:00
|
|
|
|
2022-01-23 22:47:41 +00:00
|
|
|
[[nodiscard]] int compare(verify_node const& that) const
|
2021-09-27 22:07:58 +00:00
|
|
|
{
|
|
|
|
// higher priority comes before lower priority
|
|
|
|
auto const pa = tr_torrentGetPriority(torrent);
|
|
|
|
auto const pb = tr_torrentGetPriority(that.torrent);
|
|
|
|
if (pa != pb)
|
|
|
|
{
|
|
|
|
return pa > pb ? -1 : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// smaller torrents come before larger ones because they verify faster
|
|
|
|
if (current_size != that.current_size)
|
|
|
|
{
|
|
|
|
return current_size < that.current_size ? -1 : 1;
|
|
|
|
}
|
|
|
|
|
2021-12-29 19:20:09 +00:00
|
|
|
// tertiary compare just to ensure they don't compare equal
|
|
|
|
if (torrent->infoHash() != that.torrent->infoHash())
|
|
|
|
{
|
|
|
|
return torrent->infoHash() < that.torrent->infoHash() ? -1 : 1;
|
|
|
|
}
|
|
|
|
|
2021-09-27 22:07:58 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator<(verify_node const& that) const
|
|
|
|
{
|
|
|
|
return compare(that) < 0;
|
|
|
|
}
|
2008-02-15 16:00:46 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct verify_node currentNode;
|
2021-09-27 22:07:58 +00:00
|
|
|
// TODO: refactor s.t. this doesn't leak
|
2021-12-29 19:20:09 +00:00
|
|
|
static auto& verify_list{ *new std::set<verify_node>{} };
|
2022-01-31 19:34:04 +00:00
|
|
|
static std::optional<std::thread::id> verify_thread_id;
|
2011-03-22 15:19:54 +00:00
|
|
|
static bool stopCurrent = false;
|
2008-02-15 16:00:46 +00:00
|
|
|
|
2021-11-20 21:20:45 +00:00
|
|
|
static std::mutex verify_mutex_;
|
2008-02-15 16:00:46 +00:00
|
|
|
|
2022-01-31 19:34:04 +00:00
|
|
|
static void verifyThreadFunc()
|
2008-02-15 16:00:46 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
for (;;)
|
2008-02-15 16:00:46 +00:00
|
|
|
{
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2021-11-20 21:20:45 +00:00
|
|
|
auto const lock = std::lock_guard(verify_mutex_);
|
2008-02-15 16:00:46 +00:00
|
|
|
|
2021-11-20 21:20:45 +00:00
|
|
|
stopCurrent = false;
|
2021-12-29 19:20:09 +00:00
|
|
|
if (std::empty(verify_list))
|
2021-11-20 21:20:45 +00:00
|
|
|
{
|
|
|
|
currentNode.torrent = nullptr;
|
2022-01-31 19:34:04 +00:00
|
|
|
verify_thread_id.reset();
|
2021-12-29 19:20:09 +00:00
|
|
|
return;
|
2021-11-20 21:20:45 +00:00
|
|
|
}
|
|
|
|
|
2021-12-29 19:20:09 +00:00
|
|
|
auto const it = std::begin(verify_list);
|
2021-11-20 21:20:45 +00:00
|
|
|
currentNode = *it;
|
2021-12-29 19:20:09 +00:00
|
|
|
verify_list.erase(it);
|
2021-11-20 21:20:45 +00:00
|
|
|
}
|
2008-02-15 16:00:46 +00:00
|
|
|
|
2021-09-27 22:07:58 +00:00
|
|
|
tr_torrent* tor = currentNode.torrent;
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_logAddTorInfo(tor, "%s", _("Verifying torrent"));
|
2021-12-16 09:43:51 +00:00
|
|
|
tor->setVerifyState(TR_VERIFY_NOW);
|
2021-12-29 19:20:09 +00:00
|
|
|
auto const changed = verifyTorrent(tor, &stopCurrent);
|
2021-12-16 09:43:51 +00:00
|
|
|
tor->setVerifyState(TR_VERIFY_NONE);
|
2017-06-08 07:24:12 +00:00
|
|
|
TR_ASSERT(tr_isTorrent(tor));
|
2008-02-15 16:00:46 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (!stopCurrent && changed)
|
|
|
|
{
|
2021-12-15 21:25:42 +00:00
|
|
|
tor->setDirty();
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2013-01-31 21:58:25 +00:00
|
|
|
|
2021-09-15 00:18:09 +00:00
|
|
|
if (currentNode.callback_func != nullptr)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
(*currentNode.callback_func)(tor, stopCurrent, currentNode.callback_data);
|
|
|
|
}
|
2008-02-15 16:00:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
void tr_verifyAdd(tr_torrent* tor, tr_verify_done_func callback_func, void* callback_data)
|
2008-02-15 16:00:46 +00:00
|
|
|
{
|
2017-06-08 07:24:12 +00:00
|
|
|
TR_ASSERT(tr_isTorrent(tor));
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_logAddTorInfo(tor, "%s", _("Queued for verification"));
|
|
|
|
|
2021-09-27 22:07:58 +00:00
|
|
|
auto node = verify_node{};
|
|
|
|
node.torrent = tor;
|
|
|
|
node.callback_func = callback_func;
|
|
|
|
node.callback_data = callback_data;
|
2021-12-16 08:49:04 +00:00
|
|
|
node.current_size = tor->hasTotal();
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-11-20 21:20:45 +00:00
|
|
|
auto const lock = std::lock_guard(verify_mutex_);
|
2021-12-16 09:43:51 +00:00
|
|
|
tor->setVerifyState(TR_VERIFY_WAIT);
|
2021-12-29 19:20:09 +00:00
|
|
|
verify_list.insert(node);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-01-31 19:34:04 +00:00
|
|
|
if (!verify_thread_id)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2022-01-31 19:34:04 +00:00
|
|
|
auto thread = std::thread(verifyThreadFunc);
|
|
|
|
verify_thread_id = thread.get_id();
|
|
|
|
thread.detach();
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2008-02-15 16:00:46 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
void tr_verifyRemove(tr_torrent* tor)
|
2008-02-15 16:00:46 +00:00
|
|
|
{
|
2017-06-13 02:24:09 +00:00
|
|
|
TR_ASSERT(tr_isTorrent(tor));
|
|
|
|
|
2021-11-20 21:20:45 +00:00
|
|
|
verify_mutex_.lock();
|
2008-02-15 16:00:46 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (tor == currentNode.torrent)
|
2008-02-15 16:00:46 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
stopCurrent = true;
|
2012-12-05 17:29:46 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
while (stopCurrent)
|
2008-02-15 16:00:46 +00:00
|
|
|
{
|
2021-11-20 21:20:45 +00:00
|
|
|
verify_mutex_.unlock();
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_wait_msec(100);
|
2021-11-20 21:20:45 +00:00
|
|
|
verify_mutex_.lock();
|
2008-02-15 16:00:46 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
else
|
2008-02-15 16:00:46 +00:00
|
|
|
{
|
2021-09-27 22:07:58 +00:00
|
|
|
auto const it = std::find_if(
|
2021-12-29 19:20:09 +00:00
|
|
|
std::begin(verify_list),
|
|
|
|
std::end(verify_list),
|
2021-09-27 22:07:58 +00:00
|
|
|
[tor](auto const& task) { return tor == task.torrent; });
|
2013-02-01 00:21:30 +00:00
|
|
|
|
2021-12-16 09:43:51 +00:00
|
|
|
tor->setVerifyState(TR_VERIFY_NONE);
|
2013-02-01 00:21:30 +00:00
|
|
|
|
2021-12-29 19:20:09 +00:00
|
|
|
if (it != std::end(verify_list))
|
2013-02-01 00:21:30 +00:00
|
|
|
{
|
2021-09-27 22:07:58 +00:00
|
|
|
if (it->callback_func != nullptr)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-09-27 22:07:58 +00:00
|
|
|
(*it->callback_func)(tor, true, it->callback_data);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2013-02-01 00:21:30 +00:00
|
|
|
|
2021-12-29 19:20:09 +00:00
|
|
|
verify_list.erase(it);
|
2013-02-01 00:21:30 +00:00
|
|
|
}
|
2008-02-15 16:00:46 +00:00
|
|
|
}
|
|
|
|
|
2021-11-20 21:20:45 +00:00
|
|
|
verify_mutex_.unlock();
|
2008-02-15 16:00:46 +00:00
|
|
|
}
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2021-10-24 16:41:54 +00:00
|
|
|
void tr_verifyClose(tr_session* /*session*/)
|
2009-04-16 13:10:25 +00:00
|
|
|
{
|
2021-11-20 21:20:45 +00:00
|
|
|
auto const lock = std::lock_guard(verify_mutex_);
|
2009-04-16 13:10:25 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
stopCurrent = true;
|
2021-12-29 19:20:09 +00:00
|
|
|
verify_list.clear();
|
2009-04-16 13:10:25 +00:00
|
|
|
}
|