uint64_t tr_cpSizeWhenDone( const tr_completion * ccp ) { if( ccp->sizeWhenDoneIsDirty ) { uint64_t size = 0; const tr_torrent * tor = ccp->tor; const tr_info * inf = tr_torrentInfo( tor ); tr_completion * cp = (tr_completion *) ccp; /* mutable */ if( tr_cpHasAll( ccp ) ) { size = inf->totalSize; } else { tr_piece_index_t p; for( p=0; p<inf->pieceCount; ++p ) { uint64_t n = 0; const uint64_t pieceSize = tr_torPieceCountBytes( tor, p ); if( !inf->pieces[p].dnd ) { n = pieceSize; } else { uint64_t o = 0; tr_block_index_t b, f, l; tr_torGetPieceBlockRange( cp->tor, p, &f, &l ); for( b=f; b<=l; ++b ) if( tr_cpBlockIsComplete( cp, b ) ) n += tr_torBlockCountBytes( tor, b ); o = tr_bitfieldCountRange( &cp->blockBitfield, f, l+1 ); o *= cp->tor->blockSize; if( l == ( cp->tor->blockCount - 1 ) && tr_bitfieldHas( &cp->blockBitfield, l ) ) o -= ( cp->tor->blockSize - cp->tor->lastBlockSize ); assert( n == o ); } assert( n <= tr_torPieceCountBytes( tor, p ) ); size += n; } } assert( size <= inf->totalSize ); assert( size >= cp->sizeNow ); cp->sizeWhenDoneLazy = size; cp->sizeWhenDoneIsDirty = false; } return ccp->sizeWhenDoneLazy; }
uint64_t tr_cpSizeWhenDone(tr_completion const* ccp) { if (ccp->sizeWhenDoneIsDirty) { uint64_t size = 0; tr_torrent const* tor = ccp->tor; tr_info const* inf = tr_torrentInfo(tor); tr_completion* cp = (tr_completion*)ccp; /* mutable */ if (tr_cpHasAll(ccp)) { size = inf->totalSize; } else { for (tr_piece_index_t p = 0; p < inf->pieceCount; ++p) { uint64_t n = 0; uint64_t const pieceSize = tr_torPieceCountBytes(tor, p); if (!inf->pieces[p].dnd) { n = pieceSize; } else { tr_block_index_t f; tr_block_index_t l; tr_torGetPieceBlockRange(cp->tor, p, &f, &l); n = tr_bitfieldCountRange(&cp->blockBitfield, f, l + 1); n *= cp->tor->blockSize; if (l == cp->tor->blockCount - 1 && tr_bitfieldHas(&cp->blockBitfield, l)) { n -= cp->tor->blockSize - cp->tor->lastBlockSize; } } TR_ASSERT(n <= tr_torPieceCountBytes(tor, p)); size += n; } } TR_ASSERT(size <= inf->totalSize); TR_ASSERT(size >= cp->sizeNow); cp->sizeWhenDoneLazy = size; cp->sizeWhenDoneIsDirty = false; } return ccp->sizeWhenDoneLazy; }
size_t tr_cpMissingBytesInPiece (const tr_completion * cp, tr_piece_index_t piece) { if (tr_cpHasAll (cp)) { return 0; } else { size_t haveBytes = 0; tr_block_index_t f, l; const size_t pieceByteSize = tr_torPieceCountBytes (cp->tor, piece); tr_torGetPieceBlockRange (cp->tor, piece, &f, &l); if (f != l) { /* nb: we don't pass the usual l+1 here to tr_bitfieldCountRange (). It's faster to handle the last block separately because its size needs to be checked separately. */ haveBytes = tr_bitfieldCountRange (&cp->blockBitfield, f, l); haveBytes *= cp->tor->blockSize; } if (tr_bitfieldHas (&cp->blockBitfield, l)) /* handle the last block */ haveBytes += tr_torBlockCountBytes (cp->tor, l); assert (haveBytes <= pieceByteSize); return pieceByteSize - haveBytes; } }
static int recalculateHash( const tr_torrent * tor, tr_piece_index_t pieceIndex, uint8_t * setme ) { size_t bytesLeft; uint32_t offset = 0; int success = TRUE; SHA_CTX sha; assert( tor ); assert( setme ); assert( pieceIndex < tor->info.pieceCount ); SHA1_Init( &sha ); bytesLeft = tr_torPieceCountBytes( tor, pieceIndex ); while( bytesLeft ) { uint8_t buf[8192]; const int len = MIN( bytesLeft, sizeof( buf ) ); success = !tr_ioRead( tor, pieceIndex, offset, len, buf ); if( !success ) break; SHA1_Update( &sha, buf, len ); offset += len; bytesLeft -= len; } if( success ) SHA1_Final( setme, &sha ); return success; }
/* returns 0 on success, or an errno on failure */ static int readOrWritePiece( const tr_torrent * tor, int ioMode, tr_piece_index_t pieceIndex, uint32_t pieceOffset, uint8_t * buf, size_t buflen ) { int err = 0; tr_file_index_t fileIndex; uint64_t fileOffset; const tr_info * info = &tor->info; if( pieceIndex >= tor->info.pieceCount ) return EINVAL; if( pieceOffset + buflen > tr_torPieceCountBytes( tor, pieceIndex ) ) return EINVAL; tr_ioFindFileLocation( tor, pieceIndex, pieceOffset, &fileIndex, &fileOffset ); while( buflen && !err ) { const tr_file * file = &info->files[fileIndex]; const uint64_t bytesThisPass = MIN( buflen, file->length - fileOffset ); err = readOrWriteBytes( tor, ioMode, fileIndex, fileOffset, buf, bytesThisPass ); buf += bytesThisPass; buflen -= bytesThisPass; ++fileIndex; fileOffset = 0; } return err; }
uint64_t tr_cpSizeWhenDone( const tr_completion * ccp ) { if( ccp->sizeWhenDoneIsDirty ) { tr_completion * cp = (tr_completion *) ccp; /* mutable */ const tr_torrent * tor = cp->tor; const tr_info * info = &tor->info; tr_piece_index_t i; uint64_t size = 0; for( i = 0; i < info->pieceCount; ++i ) { if( !info->pieces[i].dnd ) { /* we want the piece... */ size += tr_torPieceCountBytes( tor, i ); } else if( tr_cpPieceIsComplete( cp, i ) ) { /* we have the piece... */ size += tr_torPieceCountBytes( tor, i ); } else if( cp->completeBlocks[i] ) { /* we have part of the piece... */ const tr_block_index_t b = tr_torPieceFirstBlock( tor, i ); const tr_block_index_t e = b + tr_torPieceCountBlocks( tor, i ); tr_block_index_t j; for( j = b; j < e; ++j ) if( tr_cpBlockIsComplete( cp, j ) ) size += tr_torBlockCountBytes( tor, j ); } } cp->sizeWhenDoneLazy = size; cp->sizeWhenDoneIsDirty = 0; } assert( ccp->sizeWhenDoneLazy <= ccp->tor->info.totalSize ); assert( ccp->sizeWhenDoneLazy >= ccp->sizeNow ); return ccp->sizeWhenDoneLazy; }
uint64_t tr_cpHaveValid( const tr_completion * ccp ) { if( ccp->haveValidIsDirty ) { tr_piece_index_t i; uint64_t size = 0; tr_completion * cp = (tr_completion *) ccp; /* mutable */ const tr_torrent * tor = ccp->tor; const tr_info * info = &tor->info; for( i=0; i<info->pieceCount; ++i ) if( tr_cpPieceIsComplete( ccp, i ) ) size += tr_torPieceCountBytes( tor, i ); cp->haveValidLazy = size; cp->haveValidIsDirty = false; } return ccp->haveValidLazy; }
static tr_bool recalculateHash( tr_torrent * tor, tr_piece_index_t pieceIndex, uint8_t * setme ) { size_t bytesLeft; uint32_t offset = 0; tr_bool success = TRUE; const size_t buflen = tor->blockSize; void * buffer = tr_valloc( buflen ); SHA_CTX sha; assert( tor != NULL ); assert( pieceIndex < tor->info.pieceCount ); assert( buffer != NULL ); assert( buflen > 0 ); assert( setme != NULL ); SHA1_Init( &sha ); bytesLeft = tr_torPieceCountBytes( tor, pieceIndex ); tr_ioPrefetch( tor, pieceIndex, offset, bytesLeft ); while( bytesLeft ) { const int len = MIN( bytesLeft, buflen ); success = !tr_cacheReadBlock( tor->session->cache, tor, pieceIndex, offset, len, buffer ); if( !success ) break; SHA1_Update( &sha, buffer, len ); offset += len; bytesLeft -= len; } if( success ) SHA1_Final( setme, &sha ); tr_free( buffer ); return success; }
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; }
static bool verifyTorrent(tr_torrent* tor, bool* stopFlag) { time_t end; tr_sha1_ctx_t sha; 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; time_t const begin = tr_time(); size_t const buflen = 1024 * 128; /* 128 KiB buffer */ uint8_t* buffer = tr_valloc(buflen); sha = tr_sha1_init(); tr_logAddTorDbg(tor, "%s", "verifying torrent..."); tr_torrentSetChecked(tor, 0); while (!*stopFlag && pieceIndex < tor->info.pieceCount) { uint64_t leftInPiece; uint64_t bytesThisPass; uint64_t leftInFile; tr_file const* file = &tor->info.files[fileIndex]; /* if we're starting a new piece... */ if (piecePos == 0) { hadPiece = tr_torrentPieceIsComplete(tor, pieceIndex); } /* if we're starting a new file... */ if (filePos == 0 && fd == TR_BAD_SYS_FILE && fileIndex != prevFileIndex) { char* filename = tr_torrentFindFile(tor, fileIndex); fd = filename == NULL ? TR_BAD_SYS_FILE : tr_sys_file_open(filename, TR_SYS_FILE_READ | TR_SYS_FILE_SEQUENTIAL, 0, NULL); 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 != TR_BAD_SYS_FILE) { uint64_t numRead; if (tr_sys_file_read_at(fd, buffer, bytesThisPass, filePos, &numRead, NULL) && numRead > 0) { bytesThisPass = numRead; tr_sha1_update(sha, buffer, bytesThisPass); #if defined HAVE_POSIX_FADVISE && defined POSIX_FADV_DONTNEED (void)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; bool hasPiece; uint8_t hash[SHA_DIGEST_LENGTH]; tr_sha1_final(sha, hash); hasPiece = memcmp(hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH) == 0; 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); } sha = tr_sha1_init(); pieceIndex++; piecePos = 0; } /* if we're finishing a file... */ if (leftInFile == 0) { if (fd != TR_BAD_SYS_FILE) { tr_sys_file_close(fd, NULL); fd = TR_BAD_SYS_FILE; } fileIndex++; filePos = 0; } } /* cleanup */ if (fd != TR_BAD_SYS_FILE) { tr_sys_file_close(fd, NULL); } tr_sha1_final(sha, NULL); free(buffer); /* stopwatch */ end = tr_time(); 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)))); return changed; }
static tr_bool verifyTorrent( tr_torrent * tor, tr_bool * stopFlag ) { SHA_CTX sha; int fd = -1; int64_t filePos = 0; tr_bool changed = 0; tr_bool hadPiece = 0; uint32_t piecePos = 0; uint32_t pieceBytesRead = 0; tr_file_index_t fileIndex = 0; tr_piece_index_t pieceIndex = 0; const int64_t buflen = tor->info.pieceSize; uint8_t * buffer = tr_new( uint8_t, buflen ); #ifdef STOPWATCH time_t now = time( NULL ); #endif SHA1_Init( &sha ); while( !*stopFlag && ( pieceIndex < tor->info.pieceCount ) ) { int64_t leftInPiece; int64_t leftInFile; int64_t bytesThisPass; const tr_file * file = &tor->info.files[fileIndex]; /* if we're starting a new piece... */ if( piecePos == 0 ) { hadPiece = tr_cpPieceIsComplete( &tor->completion, pieceIndex ); /* fprintf( stderr, "starting piece %d of %d\n", (int)pieceIndex, (int)tor->info.pieceCount ); */ } /* if we're starting a new file... */ if( !filePos && (fd<0) ) { char * filename = tr_buildPath( tor->downloadDir, file->name, NULL ); fd = tr_open_file_for_scanning( filename ); /* fprintf( stderr, "opening file #%d (%s) -- %d\n", fileIndex, filename, fd ); */ tr_free( filename ); } /* 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 ); /* fprintf( stderr, "reading this pass: %d\n", (int)bytesThisPass ); */ /* read a bit */ if( (fd>=0) && tr_lseek( fd, filePos, SEEK_SET ) != -1 ) { const int64_t numRead = read( fd, buffer, bytesThisPass ); if( numRead > 0 ) pieceBytesRead += numRead; if( numRead == bytesThisPass ) SHA1_Update( &sha, buffer, numRead ); } /* move our offsets */ leftInPiece -= bytesThisPass; leftInFile -= bytesThisPass; piecePos += bytesThisPass; filePos += bytesThisPass; /* if we're finishing a piece... */ if( leftInPiece == 0 ) { tr_bool hasPiece; uint8_t hash[SHA_DIGEST_LENGTH]; SHA1_Final( hash, &sha ); hasPiece = !memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH ); /* fprintf( stderr, "do the hashes match? %s\n", (hasPiece?"yes":"no") ); */ if( hasPiece ) { tr_torrentSetHasPiece( tor, pieceIndex, TRUE ); if( !hadPiece ) changed = TRUE; } else if( hadPiece ) { tr_torrentSetHasPiece( tor, pieceIndex, FALSE ); changed = TRUE; } tr_torrentSetPieceChecked( tor, pieceIndex, TRUE ); tor->anyDate = time( NULL ); /* going full-throttle on a verify can choke other processes' * disk IO, so wait a fwe msec between pieces. * The msec is arbitrary, and the "if" clause is to make sure we * don't slow down verification of files that don't exist */ if( pieceBytesRead == tr_torPieceCountBytes( tor, pieceIndex ) ) tr_wait( 50 ); SHA1_Init( &sha ); ++pieceIndex; piecePos = 0; pieceBytesRead = 0; } /* if we're finishing a file... */ if( leftInFile == 0 ) { /* fprintf( stderr, "closing file\n" ); */ if( fd >= 0 ) { tr_close_file( fd ); fd = -1; } ++fileIndex; filePos = 0; } } /* cleanup */ if( fd >= 0 ) tr_close_file( fd ); tr_free( buffer ); #ifdef STOPWATCH { time_t now2 = time( NULL ); fprintf( stderr, "it took %d seconds to verify %"PRIu64" bytes (%"PRIu64" bytes per second)\n", (int)(now2-now), tor->info.totalSize, (uint64_t)(tor->info.totalSize/(1+(now2-now))) ); } #endif return changed; }