2008-02-15 16:00:46 +00:00
|
|
|
/*
|
2014-01-19 01:09:44 +00:00
|
|
|
* This file Copyright (C) 2007-2014 Mnemosyne LLC
|
2008-02-15 16:00:46 +00:00
|
|
|
*
|
2014-01-21 03:10:30 +00:00
|
|
|
* It may be used under the GNU GPL versions 2 or 3
|
2014-01-19 01:09:44 +00:00
|
|
|
* or any future license endorsed by Mnemosyne LLC.
|
2008-02-15 16:00:46 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2017-04-21 07:40:57 +00:00
|
|
|
#include <string.h> /* memcmp() */
|
|
|
|
#include <stdlib.h> /* free() */
|
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"
|
2008-02-15 16:00:46 +00:00
|
|
|
#include "list.h"
|
2013-01-25 23:34:20 +00:00
|
|
|
#include "log.h"
|
2017-04-21 07:40:57 +00:00
|
|
|
#include "platform.h" /* tr_lock() */
|
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
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
2009-09-06 14:05:06 +00:00
|
|
|
enum
|
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY = 100
|
2009-09-06 14:05:06 +00:00
|
|
|
};
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static bool verifyTorrent(tr_torrent* tor, bool* stopFlag)
|
2009-04-06 04:02:51 +00:00
|
|
|
{
|
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;
|
|
|
|
tr_file_index_t prevFileIndex = !fileIndex;
|
|
|
|
tr_piece_index_t pieceIndex = 0;
|
2017-04-20 16:02:19 +00:00
|
|
|
time_t const begin = tr_time();
|
2020-11-15 21:53:42 +00:00
|
|
|
size_t const buflen = 1024 * 128; // 128 KiB buffer
|
|
|
|
uint8_t* const buffer = tr_malloc(buflen);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2020-11-15 21:53:42 +00:00
|
|
|
tr_sha1_ctx_t sha = tr_sha1_init();
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
tr_logAddTorDbg(tor, "%s", "verifying torrent...");
|
|
|
|
tr_torrentSetChecked(tor, 0);
|
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
while (!*stopFlag && pieceIndex < tor->info.pieceCount)
|
2009-04-06 04:02:51 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
uint64_t leftInPiece;
|
|
|
|
uint64_t bytesThisPass;
|
|
|
|
uint64_t leftInFile;
|
2017-04-20 16:02:19 +00:00
|
|
|
tr_file const* file = &tor->info.files[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)
|
|
|
|
{
|
|
|
|
hadPiece = tr_torrentPieceIsComplete(tor, pieceIndex);
|
|
|
|
}
|
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-08-15 09:41:48 +00:00
|
|
|
fd = filename == NULL ? TR_BAD_SYS_FILE :
|
|
|
|
tr_sys_file_open(filename, TR_SYS_FILE_READ | TR_SYS_FILE_SEQUENTIAL, 0, NULL);
|
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 */
|
|
|
|
leftInPiece = tr_torPieceCountBytes(tor, pieceIndex) - piecePos;
|
|
|
|
leftInFile = file->length - filePos;
|
|
|
|
bytesThisPass = MIN(leftInFile, leftInPiece);
|
|
|
|
bytesThisPass = MIN(bytesThisPass, buflen);
|
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
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
uint64_t numRead;
|
|
|
|
|
|
|
|
if (tr_sys_file_read_at(fd, buffer, bytesThisPass, filePos, &numRead, NULL) && numRead > 0)
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
bytesThisPass = numRead;
|
|
|
|
tr_sha1_update(sha, buffer, bytesThisPass);
|
2017-07-08 13:09:37 +00:00
|
|
|
tr_sys_file_advise(fd, filePos, bytesThisPass, TR_SYS_FILE_ADVICE_DONT_NEED, NULL);
|
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
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
time_t now;
|
|
|
|
bool hasPiece;
|
|
|
|
uint8_t hash[SHA_DIGEST_LENGTH];
|
2009-04-06 04:02:51 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_sha1_final(sha, hash);
|
|
|
|
hasPiece = memcmp(hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH) == 0;
|
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
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_torrentSetHasPiece(tor, pieceIndex, hasPiece);
|
|
|
|
changed |= hasPiece != hadPiece;
|
2009-04-06 04:02:51 +00:00
|
|
|
}
|
2012-12-05 17:29:46 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_torrentSetPieceChecked(tor, pieceIndex);
|
|
|
|
now = tr_time();
|
|
|
|
tor->anyDate = now;
|
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... */
|
|
|
|
if (lastSleptAt != now)
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
lastSleptAt = now;
|
|
|
|
tr_wait_msec(MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY);
|
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();
|
|
|
|
pieceIndex++;
|
|
|
|
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
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_sys_file_close(fd, NULL);
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
tr_sys_file_close(fd, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
tr_sha1_final(sha, NULL);
|
|
|
|
free(buffer);
|
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),
|
|
|
|
tor->info.totalSize,
|
|
|
|
(uint64_t)(tor->info.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;
|
2008-02-15 16:00:46 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct verify_node currentNode;
|
2017-04-19 12:04:45 +00:00
|
|
|
static tr_list* verifyList = NULL;
|
|
|
|
static tr_thread* verifyThread = NULL;
|
2011-03-22 15:19:54 +00:00
|
|
|
static bool stopCurrent = false;
|
2008-02-15 16:00:46 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
static tr_lock* getVerifyLock(void)
|
2008-02-15 16:00:46 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
static tr_lock* lock = NULL;
|
2012-12-05 17:29:46 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (lock == NULL)
|
|
|
|
{
|
|
|
|
lock = tr_lockNew();
|
|
|
|
}
|
2012-12-05 17:29:46 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return lock;
|
2008-02-15 16:00:46 +00:00
|
|
|
}
|
|
|
|
|
2020-08-18 10:36:10 +00:00
|
|
|
static void verifyThreadFunc(void* user_data)
|
2008-02-15 16:00:46 +00:00
|
|
|
{
|
2020-08-18 10:36:10 +00:00
|
|
|
TR_UNUSED(user_data);
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
for (;;)
|
2008-02-15 16:00:46 +00:00
|
|
|
{
|
2017-04-30 16:25:26 +00:00
|
|
|
bool changed = false;
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_torrent* tor;
|
|
|
|
struct verify_node* node;
|
|
|
|
|
|
|
|
tr_lockLock(getVerifyLock());
|
|
|
|
stopCurrent = false;
|
2017-04-30 16:25:26 +00:00
|
|
|
node = verifyList != NULL ? verifyList->data : NULL;
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
if (node == NULL)
|
2008-09-23 19:11:04 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
currentNode.torrent = NULL;
|
|
|
|
break;
|
2008-02-15 16:00:46 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
currentNode = *node;
|
|
|
|
tor = currentNode.torrent;
|
|
|
|
tr_list_remove_data(&verifyList, node);
|
|
|
|
tr_free(node);
|
|
|
|
tr_lockUnlock(getVerifyLock());
|
2008-02-15 16:00:46 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_logAddTorInfo(tor, "%s", _("Verifying torrent"));
|
|
|
|
tr_torrentSetVerifyState(tor, TR_VERIFY_NOW);
|
|
|
|
changed = verifyTorrent(tor, &stopCurrent);
|
|
|
|
tr_torrentSetVerifyState(tor, 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)
|
|
|
|
{
|
|
|
|
tr_torrentSetDirty(tor);
|
|
|
|
}
|
2013-01-31 21:58:25 +00:00
|
|
|
|
2017-04-30 16:25:26 +00:00
|
|
|
if (currentNode.callback_func != NULL)
|
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
|
|
|
verifyThread = NULL;
|
|
|
|
tr_lockUnlock(getVerifyLock());
|
2008-02-15 16:00:46 +00:00
|
|
|
}
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
static int compareVerifyByPriorityAndSize(void const* va, void const* vb)
|
2010-11-11 15:31:11 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
struct verify_node const* a = va;
|
|
|
|
struct verify_node const* b = vb;
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
/* higher priority comes before lower priority */
|
2017-04-20 16:02:19 +00:00
|
|
|
tr_priority_t const pa = tr_torrentGetPriority(a->torrent);
|
|
|
|
tr_priority_t const pb = tr_torrentGetPriority(b->torrent);
|
2017-04-19 12:04:45 +00:00
|
|
|
|
|
|
|
if (pa != pb)
|
|
|
|
{
|
|
|
|
return pa > pb ? -1 : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* smaller torrents come before larger ones because they verify faster */
|
|
|
|
if (a->current_size < b->current_size)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a->current_size > b->current_size)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2010-11-11 15:31:11 +00:00
|
|
|
}
|
2010-07-19 14:44:24 +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"));
|
|
|
|
|
2017-06-13 02:24:09 +00:00
|
|
|
struct verify_node* node = tr_new(struct verify_node, 1);
|
2017-04-19 12:04:45 +00:00
|
|
|
node->torrent = tor;
|
|
|
|
node->callback_func = callback_func;
|
|
|
|
node->callback_data = callback_data;
|
|
|
|
node->current_size = tr_torrentGetCurrentSizeOnDisk(tor);
|
|
|
|
|
|
|
|
tr_lockLock(getVerifyLock());
|
|
|
|
tr_torrentSetVerifyState(tor, TR_VERIFY_WAIT);
|
|
|
|
tr_list_insert_sorted(&verifyList, node, compareVerifyByPriorityAndSize);
|
|
|
|
|
|
|
|
if (verifyThread == NULL)
|
|
|
|
{
|
|
|
|
verifyThread = tr_threadNew(verifyThreadFunc, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
tr_lockUnlock(getVerifyLock());
|
2008-02-15 16:00:46 +00:00
|
|
|
}
|
|
|
|
|
2017-04-20 16:02:19 +00:00
|
|
|
static int compareVerifyByTorrent(void const* va, void const* vb)
|
2008-02-15 16:00:46 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
struct verify_node const* a = va;
|
|
|
|
tr_torrent const* b = vb;
|
2017-04-19 12:04:45 +00:00
|
|
|
return a->torrent - b;
|
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));
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_lock* lock = getVerifyLock();
|
|
|
|
tr_lockLock(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
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_lockUnlock(lock);
|
|
|
|
tr_wait_msec(100);
|
|
|
|
tr_lockLock(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
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
struct verify_node* node = tr_list_remove(&verifyList, tor, compareVerifyByTorrent);
|
2013-02-01 00:21:30 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_torrentSetVerifyState(tor, TR_VERIFY_NONE);
|
2013-02-01 00:21:30 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
if (node != NULL)
|
2013-02-01 00:21:30 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
if (node->callback_func != NULL)
|
|
|
|
{
|
|
|
|
(*node->callback_func)(tor, true, node->callback_data);
|
|
|
|
}
|
2013-02-01 00:21:30 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_free(node);
|
2013-02-01 00:21:30 +00:00
|
|
|
}
|
2008-02-15 16:00:46 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_lockUnlock(lock);
|
2008-02-15 16:00:46 +00:00
|
|
|
}
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2020-08-18 10:36:10 +00:00
|
|
|
void tr_verifyClose(tr_session* session)
|
2009-04-16 13:10:25 +00:00
|
|
|
{
|
2020-08-18 10:36:10 +00:00
|
|
|
TR_UNUSED(session);
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_lockLock(getVerifyLock());
|
2009-04-16 13:10:25 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
stopCurrent = true;
|
|
|
|
tr_list_free(&verifyList, tr_free);
|
2009-04-16 13:10:25 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_lockUnlock(getVerifyLock());
|
2009-04-16 13:10:25 +00:00
|
|
|
}
|