static uint64_t parseProgress( tr_torrent * tor, const uint8_t * buf, uint32_t len ) { uint64_t ret = 0; if( len == FR_PROGRESS_LEN( tor ) ) { int i; int n; tr_bitfield bitfield; /* compare file mtimes */ tr_time_t * curMTimes = getMTimes( tor, &n ); const uint8_t * walk = buf; tr_time_t mtime; for( i = 0; i < n; ++i ) { readBytes( &mtime, &walk, sizeof( tr_time_t ) ); if( curMTimes[i] == mtime ) tr_torrentSetFileChecked( tor, i, TRUE ); else { tr_torrentSetFileChecked( tor, i, FALSE ); tr_tordbg( tor, "Torrent needs to be verified" ); } } free( curMTimes ); /* get the completion bitfield */ memset( &bitfield, 0, sizeof bitfield ); bitfield.byteCount = FR_BLOCK_BITFIELD_LEN( tor ); bitfield.bitCount = bitfield.byteCount * 8; bitfield.bits = (uint8_t*) walk; if( tr_cpBlockBitfieldSet( tor->completion, &bitfield ) ) ret = TR_FR_PROGRESS; else { tr_torrentUncheck( tor ); tr_tordbg( tor, "Torrent needs to be verified" ); } } /* the files whose mtimes are wrong, remove from completion pending a recheck... */ { tr_piece_index_t i; for( i = 0; i < tor->info.pieceCount; ++i ) if( !tr_torrentIsPieceChecked( tor, i ) ) tr_cpPieceRem( tor->completion, i ); } return ret; }
static uint64_t loadProgress( tr_benc * dict, tr_torrent * tor ) { uint64_t ret = 0; tr_benc * p; if( tr_bencDictFindDict( dict, KEY_PROGRESS, &p ) ) { const uint8_t * raw; size_t rawlen; tr_benc * m; size_t n; time_t * curMTimes = tr_torrentGetMTimes( tor, &n ); if( tr_bencDictFindList( p, KEY_PROGRESS_MTIMES, &m ) && ( n == tor->info.fileCount ) && ( n == tr_bencListSize( m ) ) ) { size_t i; for( i = 0; i < n; ++i ) { int64_t tmp; if( !tr_bencGetInt( tr_bencListChild( m, i ), &tmp ) ) { tr_tordbg( tor, "File #%zu needs to be verified - couldn't find benc entry", i ); tr_torrentSetFileChecked( tor, i, FALSE ); } else { const time_t t = (time_t) tmp; if( t == curMTimes[i] ) tr_torrentSetFileChecked( tor, i, TRUE ); else { tr_tordbg( tor, "File #%zu needs to be verified - times %lu and %lu don't match", i, t, curMTimes[i] ); tr_torrentSetFileChecked( tor, i, FALSE ); } } } } else { tr_torrentUncheck( tor ); tr_tordbg( tor, "Torrent needs to be verified - unable to find mtimes" ); } if( tr_bencDictFindRaw( p, KEY_PROGRESS_BITFIELD, &raw, &rawlen ) ) { tr_bitfield tmp; tmp.byteCount = rawlen; tmp.bitCount = tmp.byteCount * 8; tmp.bits = (uint8_t*) raw; if( !tr_cpBlockBitfieldSet( tor->completion, &tmp ) ) { tr_torrentUncheck( tor ); tr_tordbg( tor, "Torrent needs to be verified - error loading bitfield" ); } } else { tr_torrentUncheck( tor ); tr_tordbg( tor, "Torrent needs to be verified - unable to find bitfield" ); } tr_free( curMTimes ); ret = TR_FR_PROGRESS; } return ret; }
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; 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( ); time_t end; const size_t buflen = 1024 * 128; /* 128 KiB buffer */ uint8_t * buffer = tr_valloc( buflen ); tr_torrentUncheck( tor ); SHA1_Init( &sha ); 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 ); /* 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) && (fileIndex!=prevFileIndex) ) { char * filename = tr_torrentFindFile( tor, fileIndex ); fd = filename == NULL ? -1 : tr_open_file_for_scanning( filename ); /* fprintf( stderr, "opening file #%d (%s) -- %d\n", fileIndex, filename, fd ); */ 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 ); /* fprintf( stderr, "reading this pass: %d\n", (int)bytesThisPass ); */ /* 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 ); /* 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 ); 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 ) { /* 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 ); free( buffer ); /* stopwatch */ end = tr_time( ); tr_tordbg( tor, "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; }