From 6fb57dcd59aef7c3726cddf7cf09e0532c1f9675 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 15 Feb 2008 16:00:46 +0000 Subject: [PATCH] modify "verify local data" to not lose the blocks in incomplete pieces --- libtransmission/Makefile.am | 6 +- libtransmission/inout.c | 201 +++-------------------------------- libtransmission/inout.h | 38 ++++--- libtransmission/torrent.c | 13 +-- libtransmission/torrent.h | 10 +- libtransmission/verify.c | 203 ++++++++++++++++++++++++++++++++++++ libtransmission/verify.h | 23 ++++ 7 files changed, 275 insertions(+), 219 deletions(-) create mode 100644 libtransmission/verify.c create mode 100644 libtransmission/verify.h diff --git a/libtransmission/Makefile.am b/libtransmission/Makefile.am index cf2654a62..370577609 100644 --- a/libtransmission/Makefile.am +++ b/libtransmission/Makefile.am @@ -33,7 +33,8 @@ libtransmission_a_SOURCES = \ transmission.c \ trevent.c \ upnp.c \ - utils.c + utils.c \ + verify.c noinst_HEADERS = \ bencode.h \ @@ -68,7 +69,8 @@ noinst_HEADERS = \ trcompat.h \ trevent.h \ upnp.h \ - utils.h + utils.h \ + verify.h diff --git a/libtransmission/inout.c b/libtransmission/inout.c index 260b1c41b..c79db6f2e 100644 --- a/libtransmission/inout.c +++ b/libtransmission/inout.c @@ -198,13 +198,13 @@ readOrWritePiece( tr_torrent * tor, } tr_errno -tr_ioRead( tr_torrent * tor, - int pieceIndex, - int begin, - int len, - uint8_t * buf ) +tr_ioRead( const tr_torrent * tor, + int pieceIndex, + int begin, + int len, + uint8_t * buf ) { - return readOrWritePiece( tor, TR_IO_READ, pieceIndex, begin, buf, len ); + return readOrWritePiece( (tr_torrent*)tor, TR_IO_READ, pieceIndex, begin, buf, len ); } tr_errno @@ -222,9 +222,9 @@ tr_ioWrite( tr_torrent * tor, ****/ static int -tr_ioRecalculateHash( tr_torrent * tor, - int pieceIndex, - uint8_t * setme ) +tr_ioRecalculateHash( const tr_torrent * tor, + int pieceIndex, + uint8_t * setme ) { int offset; int bytesLeft; @@ -256,8 +256,8 @@ tr_ioRecalculateHash( tr_torrent * tor, return 0; } -static int -checkPiece( tr_torrent * tor, int pieceIndex ) +int +tr_ioTestPiece( const tr_torrent * tor, int pieceIndex ) { uint8_t hash[SHA_DIGEST_LENGTH]; const int ret = tr_ioRecalculateHash( tor, pieceIndex, hash ) @@ -267,44 +267,11 @@ checkPiece( tr_torrent * tor, int pieceIndex ) return ret; } -static void -checkFile( tr_torrent * tor, - int fileIndex, - int * abortFlag ) -{ - int i; - int nofile; - struct stat sb; - char path[MAX_PATH_LENGTH]; - const tr_file * file = &tor->info.files[fileIndex]; - - tr_buildPath ( path, sizeof(path), tor->destination, file->name, NULL ); - nofile = stat( path, &sb ) || !S_ISREG( sb.st_mode ); - - for( i=file->firstPiece; i<=file->lastPiece && iinfo.pieceCount && (!*abortFlag); ++i ) - { - if( nofile ) - { - tr_torrentSetHasPiece( tor, i, 0 ); - } - else if( !tr_torrentIsPieceChecked( tor, i ) ) - { - const int check = checkPiece( tor, i ); - tr_torrentSetHasPiece( tor, i, !check ); - tr_torrentSetPieceChecked( tor, i, TRUE ); - } - } -} - -/** -*** -**/ - int tr_ioHash( tr_torrent * tor, int pieceIndex ) { int ret; - const int success = !checkPiece( tor, pieceIndex ); + const int success = !tr_ioTestPiece( tor, pieceIndex ); if( success ) { @@ -324,147 +291,3 @@ tr_ioHash( tr_torrent * tor, int pieceIndex ) return ret; } - -/** -*** -**/ - -struct recheck_node -{ - tr_torrent * torrent; - tr_recheck_done_cb recheck_done_cb; -}; - -static void -fireCheckDone( tr_torrent * torrent, - tr_recheck_done_cb recheck_done_cb ) -{ - if( recheck_done_cb != NULL ) - (*recheck_done_cb)( torrent ); -} - -static struct recheck_node currentNode; - -static tr_list * recheckList = NULL; - -static tr_thread * recheckThread = NULL; - -static int stopCurrent = FALSE; - -static tr_lock* getRecheckLock( void ) -{ - static tr_lock * lock = NULL; - if( lock == NULL ) - lock = tr_lockNew( ); - return lock; -} - -static void -recheckThreadFunc( void * unused UNUSED ) -{ - for( ;; ) - { - int i; - tr_torrent * tor; - struct recheck_node * node; - - tr_lockLock( getRecheckLock( ) ); - stopCurrent = FALSE; - node = (struct recheck_node*) recheckList ? recheckList->data : NULL; - if( node == NULL ) { - currentNode.torrent = NULL; - break; - } - - currentNode = *node; - tor = currentNode.torrent; - tr_list_remove_data( &recheckList, node ); - tr_free( node ); - tr_lockUnlock( getRecheckLock( ) ); - - tor->recheckState = TR_RECHECK_NOW; - - /* remove the unchecked pieces from completion... */ - for( i=0; iinfo.pieceCount; ++i ) - if( !tr_torrentIsPieceChecked( tor, i ) ) - tr_cpPieceRem( tor->completion, i ); - - tr_inf( "Verifying some pieces of \"%s\"", tor->info.name ); - for( i=0; iinfo.fileCount && !stopCurrent; ++i ) - checkFile( tor, i, &stopCurrent ); - - tor->recheckState = TR_RECHECK_NONE; - - if( !stopCurrent ) - { - tr_fastResumeSave( tor ); - fireCheckDone( tor, currentNode.recheck_done_cb ); - } - } - - recheckThread = NULL; - tr_lockUnlock( getRecheckLock( ) ); -} - -void -tr_ioRecheckAdd( tr_torrent * tor, - tr_recheck_done_cb recheck_done_cb ) -{ - const int uncheckedCount = tr_torrentCountUncheckedPieces( tor ); - - if( !uncheckedCount ) - { - /* doesn't need to be checked... */ - recheck_done_cb( tor ); - } - else - { - struct recheck_node * node; - - tr_inf( "Queueing %s to verify %d local file pieces", tor->info.name, uncheckedCount ); - - node = tr_new( struct recheck_node, 1 ); - node->torrent = tor; - node->recheck_done_cb = recheck_done_cb; - - tr_lockLock( getRecheckLock( ) ); - tor->recheckState = recheckList ? TR_RECHECK_WAIT : TR_RECHECK_NOW; - tr_list_append( &recheckList, node ); - if( recheckThread == NULL ) - recheckThread = tr_threadNew( recheckThreadFunc, NULL, "recheckThreadFunc" ); - tr_lockUnlock( getRecheckLock( ) ); - } -} - -static int -compareRecheckByTorrent( const void * va, const void * vb ) -{ - const struct recheck_node * a = va; - const tr_torrent * b = vb; - return a->torrent - b; -} - -void -tr_ioRecheckRemove( tr_torrent * tor ) -{ - tr_lock * lock = getRecheckLock( ); - tr_lockLock( lock ); - - if( tor == currentNode.torrent ) - { - stopCurrent = TRUE; - while( stopCurrent ) - { - tr_lockUnlock( lock ); - tr_wait( 100 ); - tr_lockLock( lock ); - } - } - else - { - tr_free( tr_list_remove( &recheckList, tor, compareRecheckByTorrent ) ); - tor->recheckState = TR_RECHECK_NONE; - } - - tr_lockUnlock( lock ); -} diff --git a/libtransmission/inout.h b/libtransmission/inout.h index 82a6033a1..b2b0944cb 100644 --- a/libtransmission/inout.h +++ b/libtransmission/inout.h @@ -32,31 +32,35 @@ struct tr_torrent; * @return 0 on success, TR_ERROR_ASSERT if the arguments are incorrect, * or TR_ERROR_IO_* otherwise. */ -int tr_ioRead ( struct tr_torrent*, int index, int begin, int len, uint8_t * ); +int tr_ioRead ( const struct tr_torrent * tor, + int pieceIndex, + int offset, + int len, + uint8_t * setme ); /** * Writes the block specified by the piece index, offset, and length. * @return 0 on success, TR_ERROR_ASSERT if the arguments are incorrect, * or TR_ERROR_IO_* otherwise. */ -tr_errno tr_ioWrite ( struct tr_torrent *, int index, int begin, int len, const uint8_t * ); +tr_errno tr_ioWrite ( struct tr_torrent * tor, + int pieceIndex, + int offset, + int len, + const uint8_t * writeme ); -/* hashes the specified piece and updates the completion accordingly. */ +/** + * returns true if the piece matches its metainfo's SHA1 checksum, + * false otherwise. + */ +int tr_ioTestPiece( const tr_torrent*, int piece ); + + +/** + * tests the specified piece and uses the results to + * update the torrent's "completion" and "blame" fields. + */ int tr_ioHash ( tr_torrent*, int piece ); -/** -*** -**/ - -typedef void (*tr_recheck_done_cb)( tr_torrent * tor ); - -void tr_ioRecheckAdd( tr_torrent * tor, - tr_recheck_done_cb recheck_done_cb ); - -void tr_ioRecheckRemove( tr_torrent * tor ); - -/** -*** -**/ #endif diff --git a/libtransmission/torrent.c b/libtransmission/torrent.c index 49cc1db3f..cf29d1136 100644 --- a/libtransmission/torrent.c +++ b/libtransmission/torrent.c @@ -48,6 +48,7 @@ #include "trcompat.h" /* for strlcpy */ #include "trevent.h" #include "utils.h" +#include "verify.h" /*** **** @@ -600,9 +601,9 @@ tr_torrentStat( tr_torrent * tor ) s->percentDone = tr_cpPercentDone( tor->completion ); s->leftUntilDone = tr_cpLeftUntilDone( tor->completion ); - if( tor->recheckState == TR_RECHECK_NOW ) + if( tor->verifyState == TR_VERIFY_NOW ) s->status = TR_STATUS_CHECK; - else if( tor->recheckState == TR_RECHECK_WAIT ) + else if( tor->verifyState == TR_VERIFY_WAIT ) s->status = TR_STATUS_CHECK_WAIT; else if( !tor->isRunning ) s->status = TR_STATUS_STOPPED; @@ -948,7 +949,7 @@ tr_torrentStart( tr_torrent * tor ) { tr_fastResumeLoad( tor, TR_FR_PROGRESS, NULL ); tor->isRunning = 1; - tr_ioRecheckAdd( tor, checkAndStartCB ); + tr_verifyAdd( tor, checkAndStartCB ); } tr_globalUnlock( tor->handle ); @@ -969,9 +970,9 @@ tr_torrentRecheck( tr_torrent * tor ) { tr_globalLock( tor->handle ); - tr_ioRecheckRemove( tor ); + tr_verifyRemove( tor ); tr_torrentUncheck( tor ); - tr_ioRecheckAdd( tor, torrentRecheckDoneCB ); + tr_verifyAdd( tor, torrentRecheckDoneCB ); tr_globalUnlock( tor->handle ); } @@ -983,7 +984,7 @@ stopTorrent( void * vtor ) int i; tr_torrent * tor = vtor; - tr_ioRecheckRemove( tor ); + tr_verifyRemove( tor ); tr_peerMgrStopTorrent( tor->handle->peerMgr, tor->info.hash ); tr_trackerStop( tor->tracker ); fireActiveChange( tor, 0 ); diff --git a/libtransmission/torrent.h b/libtransmission/torrent.h index b809fa4ba..d5721e045 100644 --- a/libtransmission/torrent.h +++ b/libtransmission/torrent.h @@ -91,11 +91,11 @@ void tr_torrentUncheck ( tr_torrent * ); typedef enum { - TR_RECHECK_NONE, - TR_RECHECK_WAIT, - TR_RECHECK_NOW + TR_VERIFY_NONE, + TR_VERIFY_WAIT, + TR_VERIFY_NOW } -tr_recheck_state; +tr_verify_state; struct tr_torrent { @@ -156,7 +156,7 @@ struct tr_torrent uint16_t maxConnectedPeers; - tr_recheck_state recheckState; + tr_verify_state verifyState; time_t lastStatTime; tr_stat stats[2]; diff --git a/libtransmission/verify.c b/libtransmission/verify.c new file mode 100644 index 000000000..a5ce6a1de --- /dev/null +++ b/libtransmission/verify.c @@ -0,0 +1,203 @@ +/* + * This file Copyright (C) 2007-2008 Charles Kerr + * + * 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: inout.c 4886 2008-02-01 01:54:04Z charles $ + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#include "transmission.h" +#include "completion.h" +#include "crypto.h" +#include "fastresume.h" +#include "fdlimit.h" +#include "inout.h" +#include "list.h" +#include "platform.h" +#include "peer-mgr.h" +#include "stats.h" +#include "torrent.h" +#include "utils.h" +#include "verify.h" + +/** +*** +**/ + +struct verify_node +{ + tr_torrent * torrent; + tr_verify_done_cb verify_done_cb; +}; + +static void +fireCheckDone( tr_torrent * torrent, + tr_verify_done_cb verify_done_cb ) +{ + if( verify_done_cb != NULL ) + (*verify_done_cb)( torrent ); +} + +static struct verify_node currentNode; + +static tr_list * verifyList = NULL; + +static tr_thread * verifyThread = NULL; + +static int stopCurrent = FALSE; + +static tr_lock* getVerifyLock( void ) +{ + static tr_lock * lock = NULL; + if( lock == NULL ) + lock = tr_lockNew( ); + return lock; +} + +static void +checkFile( tr_torrent * tor, + int fileIndex, + int * abortFlag ) +{ + int i; + int nofile; + struct stat sb; + char path[MAX_PATH_LENGTH]; + const tr_file * file = &tor->info.files[fileIndex]; + + tr_buildPath ( path, sizeof(path), tor->destination, file->name, NULL ); + nofile = stat( path, &sb ) || !S_ISREG( sb.st_mode ); + + for( i=file->firstPiece; i<=file->lastPiece && iinfo.pieceCount && (!*abortFlag); ++i ) + { + if( nofile ) + { + tr_torrentSetHasPiece( tor, i, 0 ); + } + else if( !tr_torrentIsPieceChecked( tor, i ) ) + { + const int check = tr_ioTestPiece( tor, i ); + tr_torrentSetHasPiece( tor, i, !check ); + tr_torrentSetPieceChecked( tor, i, TRUE ); + } + } +} + +static void +verifyThreadFunc( void * unused UNUSED ) +{ + for( ;; ) + { + int i; + 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( ) ); + + tor->verifyState = TR_VERIFY_NOW; + + tr_inf( "Verifying some pieces of \"%s\"", tor->info.name ); + for( i=0; iinfo.fileCount && !stopCurrent; ++i ) + checkFile( tor, i, &stopCurrent ); + + tor->verifyState = TR_VERIFY_NONE; + + if( !stopCurrent ) + { + tr_fastResumeSave( tor ); + fireCheckDone( tor, currentNode.verify_done_cb ); + } + } + + verifyThread = NULL; + tr_lockUnlock( getVerifyLock( ) ); +} + +void +tr_verifyAdd( tr_torrent * tor, + tr_verify_done_cb verify_done_cb ) +{ + const int uncheckedCount = tr_torrentCountUncheckedPieces( tor ); + + if( !uncheckedCount ) + { + /* doesn't need to be checked... */ + verify_done_cb( tor ); + } + else + { + struct verify_node * node; + + tr_inf( "Queueing %s to verify %d local file pieces", tor->info.name, uncheckedCount ); + + node = tr_new( struct verify_node, 1 ); + node->torrent = tor; + node->verify_done_cb = verify_done_cb; + + tr_lockLock( getVerifyLock( ) ); + tor->verifyState = verifyList ? TR_VERIFY_WAIT : TR_VERIFY_NOW; + tr_list_append( &verifyList, node ); + if( verifyThread == NULL ) + verifyThread = tr_threadNew( verifyThreadFunc, NULL, "verifyThreadFunc" ); + 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 ); + + if( tor == currentNode.torrent ) + { + stopCurrent = TRUE; + while( stopCurrent ) + { + tr_lockUnlock( lock ); + tr_wait( 100 ); + tr_lockLock( lock ); + } + } + else + { + tr_free( tr_list_remove( &verifyList, tor, compareVerifyByTorrent ) ); + tor->verifyState = TR_VERIFY_NONE; + } + + tr_lockUnlock( lock ); +} diff --git a/libtransmission/verify.h b/libtransmission/verify.h new file mode 100644 index 000000000..e89b181f2 --- /dev/null +++ b/libtransmission/verify.h @@ -0,0 +1,23 @@ +/* + * This file Copyright (C) 2007-2008 Charles Kerr + * + * 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:$ + */ + +#ifndef TR_VERIFY_H +#define TR_VERIFY_H 1 + +typedef void (*tr_verify_done_cb)( tr_torrent * tor ); + +void tr_verifyAdd( tr_torrent * tor, + tr_verify_done_cb recheck_done_cb ); + +void tr_verifyRemove( tr_torrent * tor ); + +#endif