mirror of
https://github.com/transmission/transmission
synced 2025-03-03 10:15:45 +00:00
modify "verify local data" to not lose the blocks in incomplete pieces
This commit is contained in:
parent
e2277c97e6
commit
6fb57dcd59
7 changed files with 275 additions and 219 deletions
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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 && i<tor->info.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; i<tor->info.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; i<tor->info.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 );
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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];
|
||||
|
|
203
libtransmission/verify.c
Normal file
203
libtransmission/verify.c
Normal file
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* This file Copyright (C) 2007-2008 Charles Kerr <charles@rebelbase.com>
|
||||
*
|
||||
* 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 <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#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 && i<tor->info.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; i<tor->info.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 );
|
||||
}
|
23
libtransmission/verify.h
Normal file
23
libtransmission/verify.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* This file Copyright (C) 2007-2008 Charles Kerr <charles@rebelbase.com>
|
||||
*
|
||||
* 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
|
Loading…
Reference in a new issue