1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2024-12-26 09:37:56 +00:00
transmission/libtransmission/verify.c
Jordan Lee 879a2afcbd Update the copyright year in the source code comments.
The Berne Convention says that the copyright year is moot, so instead of adding another year to each file as in previous years, I've removed the year altogether from the source code comments in libtransmission, gtk, qt, utils, daemon, and cli.

Juliusz's copyright notice in tr-dht and Johannes' copyright notice in tr-lpd have been left alone; it didn't seem appropriate to modify them.
2011-01-19 13:48:47 +00:00

316 lines
8.4 KiB
C

/*
* This file Copyright (C) Mnemosyne LLC
*
* This file is licensed by the GPL version 2. Works owned by the
* Transmission project are granted a special exemption to clause 2(b)
* so that the bulk of its code can remain under the MIT license.
* This exemption does not extend to derived works not owned by
* the Transmission project.
*
* $Id$
*/
#ifdef HAVE_POSIX_FADVISE
#define _XOPEN_SOURCE 600
#include <fcntl.h> /* posix_fadvise() */
#endif
#include <openssl/sha.h>
#include "transmission.h"
#include "completion.h"
#include "fdlimit.h"
#include "list.h"
#include "platform.h" /* tr_lock() */
#include "torrent.h"
#include "utils.h" /* tr_valloc(), tr_free() */
#include "verify.h"
/***
****
***/
enum
{
MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY = 100
};
static tr_bool
verifyTorrent( tr_torrent * tor, tr_bool * stopFlag )
{
time_t end;
SHA_CTX sha;
int fd = -1;
int64_t filePos = 0;
tr_bool changed = 0;
tr_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_tordbg( 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;
tr_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_tordbg( 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;
}
/***
****
***/
struct verify_node
{
tr_torrent * torrent;
tr_verify_done_cb verify_done_cb;
uint64_t current_size;
};
static void
fireCheckDone( tr_torrent * tor, tr_verify_done_cb verify_done_cb )
{
assert( tr_isTorrent( tor ) );
if( verify_done_cb )
verify_done_cb( tor );
}
static struct verify_node currentNode;
static tr_list * verifyList = NULL;
static tr_thread * verifyThread = NULL;
static tr_bool stopCurrent = FALSE;
static tr_lock*
getVerifyLock( void )
{
static tr_lock * lock = NULL;
if( lock == NULL )
lock = tr_lockNew( );
return lock;
}
static void
verifyThreadFunc( void * unused UNUSED )
{
for( ;; )
{
int changed = 0;
tr_torrent * tor;
struct verify_node * node;
tr_lockLock( getVerifyLock( ) );
stopCurrent = FALSE;
node = (struct verify_node*) verifyList ? verifyList->data : NULL;
if( node == NULL )
{
currentNode.torrent = NULL;
break;
}
currentNode = *node;
tor = currentNode.torrent;
tr_list_remove_data( &verifyList, node );
tr_free( node );
tr_lockUnlock( getVerifyLock( ) );
tr_torinf( tor, "%s", _( "Verifying torrent" ) );
tr_torrentSetVerifyState( tor, TR_VERIFY_NOW );
changed = verifyTorrent( tor, &stopCurrent );
tr_torrentSetVerifyState( tor, TR_VERIFY_NONE );
assert( tr_isTorrent( tor ) );
if( !stopCurrent )
{
if( changed )
tr_torrentSetDirty( tor );
fireCheckDone( tor, currentNode.verify_done_cb );
}
}
verifyThread = NULL;
tr_lockUnlock( getVerifyLock( ) );
}
static int
compareVerifyByPriorityAndSize( const void * va, const void * vb )
{
const struct verify_node * a = va;
const struct verify_node * b = vb;
/* higher priority comes before lower priority */
const tr_priority_t pa = tr_torrentGetPriority( a->torrent );
const tr_priority_t pb = tr_torrentGetPriority( b->torrent );
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;
}
void
tr_verifyAdd( tr_torrent * tor, tr_verify_done_cb verify_done_cb )
{
struct verify_node * node;
assert( tr_isTorrent( tor ) );
tr_torinf( tor, "%s", _( "Queued for verification" ) );
node = tr_new( struct verify_node, 1 );
node->torrent = tor;
node->verify_done_cb = verify_done_cb;
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( ) );
}
static int
compareVerifyByTorrent( const void * va, const void * vb )
{
const struct verify_node * a = va;
const tr_torrent * b = vb;
return a->torrent - b;
}
void
tr_verifyRemove( tr_torrent * tor )
{
tr_lock * lock = getVerifyLock( );
tr_lockLock( lock );
assert( tr_isTorrent( tor ) );
if( tor == currentNode.torrent )
{
stopCurrent = TRUE;
while( stopCurrent )
{
tr_lockUnlock( lock );
tr_wait_msec( 100 );
tr_lockLock( lock );
}
}
else
{
tr_free( tr_list_remove( &verifyList, tor, compareVerifyByTorrent ) );
tr_torrentSetVerifyState( tor, TR_VERIFY_NONE );
}
tr_lockUnlock( lock );
}
void
tr_verifyClose( tr_session * session UNUSED )
{
tr_lockLock( getVerifyLock( ) );
stopCurrent = TRUE;
tr_list_free( &verifyList, tr_free );
tr_lockUnlock( getVerifyLock( ) );
}