2023-02-11 20:49:42 +00:00
|
|
|
// This file Copyright © 2007-2023 Mnemosyne LLC.
|
2022-02-07 16:25:02 +00:00
|
|
|
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
2022-01-20 18:27:56 +00:00
|
|
|
// 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
|
|
|
|
2022-03-14 04:43:35 +00:00
|
|
|
#include <fmt/core.h>
|
|
|
|
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/transmission.h"
|
|
|
|
|
|
|
|
#include "libtransmission/completion.h"
|
|
|
|
#include "libtransmission/crypto-utils.h"
|
|
|
|
#include "libtransmission/file.h"
|
|
|
|
#include "libtransmission/log.h"
|
|
|
|
#include "libtransmission/torrent.h"
|
|
|
|
#include "libtransmission/tr-assert.h"
|
|
|
|
#include "libtransmission/utils.h" // tr_time(), tr_wait()
|
|
|
|
#include "libtransmission/verify.h"
|
2008-02-15 16:00:46 +00:00
|
|
|
|
2023-01-12 18:03:14 +00:00
|
|
|
using namespace std::chrono_literals;
|
|
|
|
|
2022-09-06 04:43:59 +00:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
2023-01-12 18:03:14 +00:00
|
|
|
auto constexpr SleepPerSecondDuringVerify = 100ms;
|
2022-09-06 04:43:59 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int tr_verify_worker::Node::compare(tr_verify_worker::Node const& that) const
|
|
|
|
{
|
|
|
|
// 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;
|
|
|
|
}
|
2009-04-06 04:02:51 +00:00
|
|
|
|
2022-09-06 04:43:59 +00:00
|
|
|
// tertiary compare just to ensure they don't compare equal
|
|
|
|
if (torrent->id() != that.torrent->id())
|
|
|
|
{
|
|
|
|
return torrent->id() < that.torrent->id() ? -1 : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2009-09-06 14:05:06 +00:00
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
bool tr_verify_worker::verify_torrent(tr_torrent* tor, std::atomic<bool> const& stop_flag)
|
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;
|
2022-03-30 18:25:41 +00:00
|
|
|
uint64_t file_pos = 0;
|
2017-04-19 12:04:45 +00:00
|
|
|
bool changed = false;
|
2022-03-30 18:25:41 +00:00
|
|
|
bool had_piece = false;
|
|
|
|
time_t last_slept_at = 0;
|
|
|
|
uint32_t piece_pos = 0;
|
|
|
|
tr_file_index_t file_index = 0;
|
|
|
|
tr_file_index_t prev_file_index = ~file_index;
|
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);
|
2022-07-31 20:58:14 +00:00
|
|
|
auto sha = tr_sha1::create();
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-03-14 04:43:35 +00:00
|
|
|
tr_logAddDebugTor(tor, "verifying torrent...");
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2023-04-23 01:25:55 +00:00
|
|
|
while (!stop_flag && piece < tor->piece_count())
|
2009-04-06 04:02:51 +00:00
|
|
|
{
|
2023-04-23 01:25:55 +00:00
|
|
|
auto const file_length = tor->file_size(file_index);
|
2009-04-06 04:02:51 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* if we're starting a new piece... */
|
2022-03-30 18:25:41 +00:00
|
|
|
if (piece_pos == 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-04-23 01:25:55 +00:00
|
|
|
had_piece = tor->has_piece(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... */
|
2022-03-30 18:25:41 +00:00
|
|
|
if (file_pos == 0 && fd == TR_BAD_SYS_FILE && file_index != prev_file_index)
|
2009-04-06 04:02:51 +00:00
|
|
|
{
|
2023-04-23 01:25:55 +00:00
|
|
|
auto const found = tor->find_file(file_index);
|
2022-04-12 15:00:02 +00:00
|
|
|
fd = !found ? TR_BAD_SYS_FILE : tr_sys_file_open(found->filename(), TR_SYS_FILE_READ | TR_SYS_FILE_SEQUENTIAL, 0);
|
2022-03-30 18:25:41 +00:00
|
|
|
prev_file_index = file_index;
|
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 */
|
2023-04-23 01:25:55 +00:00
|
|
|
uint64_t left_in_piece = tor->piece_size(piece) - piece_pos;
|
2022-03-30 18:25:41 +00:00
|
|
|
uint64_t left_in_file = file_length - file_pos;
|
|
|
|
uint64_t bytes_this_pass = std::min(left_in_file, left_in_piece);
|
|
|
|
bytes_this_pass = std::min(bytes_this_pass, 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
|
|
|
{
|
2022-03-30 18:25:41 +00:00
|
|
|
auto num_read = uint64_t{};
|
|
|
|
if (tr_sys_file_read_at(fd, std::data(buffer), bytes_this_pass, file_pos, &num_read) && num_read > 0)
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2022-03-30 18:25:41 +00:00
|
|
|
bytes_this_pass = num_read;
|
2022-07-31 20:58:14 +00:00
|
|
|
sha->add(std::data(buffer), bytes_this_pass);
|
2022-03-30 18:25:41 +00:00
|
|
|
tr_sys_file_advise(fd, file_pos, bytes_this_pass, TR_SYS_FILE_ADVICE_DONT_NEED);
|
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 */
|
2022-03-30 18:25:41 +00:00
|
|
|
left_in_piece -= bytes_this_pass;
|
|
|
|
left_in_file -= bytes_this_pass;
|
|
|
|
piece_pos += bytes_this_pass;
|
|
|
|
file_pos += bytes_this_pass;
|
2009-04-06 04:02:51 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* if we're finishing a piece... */
|
2022-03-30 18:25:41 +00:00
|
|
|
if (left_in_piece == 0)
|
2009-04-06 04:02:51 +00:00
|
|
|
{
|
2023-04-23 01:25:55 +00:00
|
|
|
if (auto const has_piece = sha->finish() == tor->piece_hash(piece); has_piece || had_piece)
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2023-04-23 01:25:55 +00:00
|
|
|
tor->set_has_piece(piece, has_piece);
|
2022-03-30 18:25:41 +00:00
|
|
|
changed |= has_piece != had_piece;
|
2009-04-06 04:02:51 +00:00
|
|
|
}
|
2012-12-05 17:29:46 +00:00
|
|
|
|
2022-02-12 19:51:43 +00:00
|
|
|
tor->checked_pieces_.set(piece, true);
|
2023-04-23 01:25:55 +00:00
|
|
|
tor->mark_changed();
|
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... */
|
2022-03-30 18:25:41 +00:00
|
|
|
if (auto const now = tr_time(); last_slept_at != now)
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2022-03-30 18:25:41 +00:00
|
|
|
last_slept_at = now;
|
2023-01-12 18:03:14 +00:00
|
|
|
tr_wait(SleepPerSecondDuringVerify);
|
2009-09-06 14:05:06 +00:00
|
|
|
}
|
2009-08-14 20:55:22 +00:00
|
|
|
|
2022-07-31 20:58:14 +00:00
|
|
|
sha->clear();
|
2021-11-25 18:26:51 +00:00
|
|
|
++piece;
|
2023-04-23 01:25:55 +00:00
|
|
|
tor->set_verify_progress(piece / float(tor->piece_count()));
|
2022-03-30 18:25:41 +00:00
|
|
|
piece_pos = 0;
|
2009-04-06 04:02:51 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* if we're finishing a file... */
|
2022-03-30 18:25:41 +00:00
|
|
|
if (left_in_file == 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
|
|
|
{
|
2022-03-27 17:37:29 +00:00
|
|
|
tr_sys_file_close(fd);
|
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
|
|
|
|
2022-03-30 18:25:41 +00:00
|
|
|
++file_index;
|
|
|
|
file_pos = 0;
|
2009-04-06 04:02:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* cleanup */
|
|
|
|
if (fd != TR_BAD_SYS_FILE)
|
|
|
|
{
|
2022-03-27 17:37:29 +00:00
|
|
|
tr_sys_file_close(fd);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* stopwatch */
|
2020-11-15 21:53:42 +00:00
|
|
|
time_t const end = tr_time();
|
2022-03-11 21:09:22 +00:00
|
|
|
tr_logAddDebugTor(
|
2021-08-15 09:41:48 +00:00
|
|
|
tor,
|
2022-03-14 04:43:35 +00:00
|
|
|
fmt::format(
|
|
|
|
"Verification is done. It took {} seconds to verify {} bytes ({} bytes per second)",
|
|
|
|
end - begin,
|
2023-04-23 01:25:55 +00:00
|
|
|
tor->total_size(),
|
|
|
|
tor->total_size() / (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
|
|
|
}
|
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
void tr_verify_worker::verify_thread_func()
|
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
|
|
|
|
2022-11-14 05:11:48 +00:00
|
|
|
if (stop_current_)
|
|
|
|
{
|
|
|
|
stop_current_ = false;
|
|
|
|
stop_current_cv_.notify_one();
|
|
|
|
}
|
|
|
|
|
2022-09-06 04:43:59 +00:00
|
|
|
if (std::empty(todo_))
|
2021-11-20 21:20:45 +00:00
|
|
|
{
|
2022-09-06 04:43:59 +00:00
|
|
|
current_node_.reset();
|
|
|
|
verify_thread_id_.reset();
|
2021-12-29 19:20:09 +00:00
|
|
|
return;
|
2021-11-20 21:20:45 +00:00
|
|
|
}
|
|
|
|
|
2022-09-06 04:43:59 +00:00
|
|
|
auto const it = std::begin(todo_);
|
|
|
|
current_node_ = *it;
|
|
|
|
todo_.erase(it);
|
2021-11-20 21:20:45 +00:00
|
|
|
}
|
2008-02-15 16:00:46 +00:00
|
|
|
|
2022-09-06 04:43:59 +00:00
|
|
|
auto* const tor = current_node_->torrent;
|
2022-03-14 04:43:35 +00:00
|
|
|
tr_logAddTraceTor(tor, "Verifying torrent");
|
2023-04-23 01:25:55 +00:00
|
|
|
tor->set_verify_state(TR_VERIFY_NOW);
|
2023-05-06 04:11:05 +00:00
|
|
|
auto const changed = verify_torrent(tor, stop_current_);
|
2023-04-23 01:25:55 +00:00
|
|
|
tor->set_verify_state(TR_VERIFY_NONE);
|
2017-06-08 07:24:12 +00:00
|
|
|
TR_ASSERT(tr_isTorrent(tor));
|
2008-02-15 16:00:46 +00:00
|
|
|
|
2022-09-06 04:43:59 +00:00
|
|
|
if (!stop_current_ && changed)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-04-23 01:25:55 +00:00
|
|
|
tor->set_dirty();
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2013-01-31 21:58:25 +00:00
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
call_callback(tor, stop_current_);
|
2008-02-15 16:00:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-06 04:43:59 +00:00
|
|
|
void tr_verify_worker::add(tr_torrent* tor)
|
2008-02-15 16:00:46 +00:00
|
|
|
{
|
2017-06-08 07:24:12 +00:00
|
|
|
TR_ASSERT(tr_isTorrent(tor));
|
2022-03-14 04:43:35 +00:00
|
|
|
tr_logAddTraceTor(tor, "Queued for verification");
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-09-06 04:43:59 +00:00
|
|
|
auto node = Node{};
|
2021-09-27 22:07:58 +00:00
|
|
|
node.torrent = tor;
|
2023-04-23 01:25:55 +00:00
|
|
|
node.current_size = tor->has_total();
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-11-20 21:20:45 +00:00
|
|
|
auto const lock = std::lock_guard(verify_mutex_);
|
2023-04-23 01:25:55 +00:00
|
|
|
tor->set_verify_state(TR_VERIFY_WAIT);
|
2022-09-06 04:43:59 +00:00
|
|
|
todo_.insert(node);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-09-06 04:43:59 +00:00
|
|
|
if (!verify_thread_id_)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-05-06 04:11:05 +00:00
|
|
|
auto thread = std::thread(&tr_verify_worker::verify_thread_func, this);
|
2022-09-06 04:43:59 +00:00
|
|
|
verify_thread_id_ = thread.get_id();
|
2022-01-31 19:34:04 +00:00
|
|
|
thread.detach();
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2008-02-15 16:00:46 +00:00
|
|
|
}
|
|
|
|
|
2022-09-06 04:43:59 +00:00
|
|
|
void tr_verify_worker::remove(tr_torrent* tor)
|
2008-02-15 16:00:46 +00:00
|
|
|
{
|
2017-06-13 02:24:09 +00:00
|
|
|
TR_ASSERT(tr_isTorrent(tor));
|
|
|
|
|
2022-11-14 05:11:48 +00:00
|
|
|
auto lock = std::unique_lock(verify_mutex_);
|
2008-02-15 16:00:46 +00:00
|
|
|
|
2022-09-06 04:43:59 +00:00
|
|
|
if (current_node_ && current_node_->torrent == tor)
|
2008-02-15 16:00:46 +00:00
|
|
|
{
|
2022-09-06 04:43:59 +00:00
|
|
|
stop_current_ = true;
|
2022-11-14 05:11:48 +00:00
|
|
|
stop_current_cv_.wait(lock, [this]() { return !stop_current_; });
|
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
|
|
|
{
|
2022-09-06 04:43:59 +00:00
|
|
|
auto const iter = std::find_if(
|
|
|
|
std::begin(todo_),
|
|
|
|
std::end(todo_),
|
2021-09-27 22:07:58 +00:00
|
|
|
[tor](auto const& task) { return tor == task.torrent; });
|
2013-02-01 00:21:30 +00:00
|
|
|
|
2023-04-23 01:25:55 +00:00
|
|
|
tor->set_verify_state(TR_VERIFY_NONE);
|
2013-02-01 00:21:30 +00:00
|
|
|
|
2022-09-06 04:43:59 +00:00
|
|
|
if (iter != std::end(todo_))
|
2013-02-01 00:21:30 +00:00
|
|
|
{
|
2023-05-06 04:11:05 +00:00
|
|
|
call_callback(tor, true);
|
2022-09-06 04:43:59 +00:00
|
|
|
todo_.erase(iter);
|
2013-02-01 00:21:30 +00:00
|
|
|
}
|
2008-02-15 16:00:46 +00:00
|
|
|
}
|
|
|
|
}
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2022-09-06 04:43:59 +00:00
|
|
|
tr_verify_worker::~tr_verify_worker()
|
2009-04-16 13:10:25 +00:00
|
|
|
{
|
2022-09-08 00:24:56 +00:00
|
|
|
{
|
|
|
|
auto const lock = std::lock_guard(verify_mutex_);
|
|
|
|
stop_current_ = true;
|
|
|
|
todo_.clear();
|
|
|
|
}
|
2022-09-06 04:43:59 +00:00
|
|
|
|
|
|
|
while (verify_thread_id_.has_value())
|
|
|
|
{
|
2023-01-12 18:03:14 +00:00
|
|
|
tr_wait(20ms);
|
2022-09-06 04:43:59 +00:00
|
|
|
}
|
2009-04-16 13:10:25 +00:00
|
|
|
}
|