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 loadDND( tr_benc * dict, tr_torrent * tor ) { uint64_t ret = 0; tr_info * inf = &tor->info; const tr_file_index_t n = inf->fileCount; tr_benc * list = NULL; if( tr_bencDictFindList( dict, KEY_DND, &list ) && ( tr_bencListSize( list ) == n ) ) { int64_t tmp; tr_file_index_t * dl = tr_new( tr_file_index_t, n ); tr_file_index_t * dnd = tr_new( tr_file_index_t, n ); tr_file_index_t i, dlCount = 0, dndCount = 0; for( i = 0; i < n; ++i ) { if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) && tmp ) dnd[dndCount++] = i; else dl[dlCount++] = i; } if( dndCount ) { tr_torrentInitFileDLs ( tor, dnd, dndCount, FALSE ); tr_tordbg( tor, "Resume file found %d files listed as dnd", dndCount ); } if( dlCount ) { tr_torrentInitFileDLs ( tor, dl, dlCount, TRUE ); tr_tordbg( tor, "Resume file found %d files marked for download", dlCount ); } tr_free( dnd ); tr_free( dl ); ret = TR_FR_DND; } else { tr_tordbg( tor, "Couldn't load DND flags. DND list (%p) has %zu children; torrent has %d files", list, tr_bencListSize( list ), (int)n ); } return ret; }
static uint64_t parsePeers( tr_torrent * tor, const uint8_t * buf, uint32_t len ) { uint64_t ret = 0; if( !tor->info.isPrivate ) { int i; const int count = len / sizeof( tr_pex ); for( i = 0; i < count; ++i ) { tr_pex pex; readBytes( &pex, &buf, sizeof( tr_pex ) ); tr_peerMgrAddPex( tor->session->peerMgr, tor->info.hash, TR_PEER_FROM_CACHE, &pex ); } tr_tordbg( tor, "Loaded %d peers from resume file", count ); ret = TR_FR_PEERS; } return ret; }
static uint64_t loadPeers( tr_benc * dict, tr_torrent * tor ) { uint64_t ret = 0; const uint8_t * str; size_t len; if( tr_bencDictFindRaw( dict, KEY_PEERS, &str, &len ) ) { int i; const int count = len / sizeof( tr_pex ); for( i = 0; i < count; ++i ) { tr_pex pex; memcpy( &pex, str + ( i * sizeof( tr_pex ) ), sizeof( tr_pex ) ); tr_peerMgrAddPex( tor->session->peerMgr, tor->info.hash, TR_PEER_FROM_CACHE, &pex ); } tr_tordbg( tor, "Loaded %d peers from resume file", count ); ret = TR_FR_PEERS; } return ret; }
static uint64_t loadPeers( tr_benc * dict, tr_torrent * tor ) { uint64_t ret = 0; const uint8_t * str; size_t len; if( tr_bencDictFindRaw( dict, KEY_PEERS, &str, &len ) ) { const int numAdded = addPeers( tor, str, len ); tr_tordbg( tor, "Loaded %d IPv4 peers from resume file", numAdded ); ret = TR_FR_PEERS; } if( tr_bencDictFindRaw( dict, KEY_PEERS6, &str, &len ) ) { const int numAdded = addPeers( tor, str, len ); tr_tordbg( tor, "Loaded %d IPv6 peers from resume file", numAdded ); ret = TR_FR_PEERS; } return ret; }
static int flushContiguous( tr_cache * cache, int pos, int n ) { int i; int err = 0; uint8_t * buf = tr_new( uint8_t, n * MAX_BLOCK_SIZE ); uint8_t * walk = buf; struct cache_block ** blocks = (struct cache_block**) tr_ptrArrayBase( &cache->blocks ); struct cache_block * b = blocks[pos]; tr_torrent * tor = b->tor; const tr_piece_index_t piece = b->piece; const uint32_t offset = b->offset; //fprintf( stderr, "flushing %d contiguous blocks [%d-%d) from cache to disk\n", n, pos, n+pos ); for( i=pos; i<pos+n; ++i ) { b = blocks[i]; memcpy( walk, b->buf, b->length ); walk += b->length; tr_free( b->buf ); tr_free( b ); } tr_ptrArrayErase( &cache->blocks, pos, pos+n ); #if 0 tr_tordbg( tor, "Writing to disk piece %d, offset %d, len %d", (int)piece, (int)offset, (int)(walk-buf) ); tr_ndbg( MY_NAME, "Removing %d blocks from cache, rank: %d - %d left", n, rank, tr_ptrArraySize(&cache->blocks) ); fprintf( stderr, "%s - Writing to disk piece %d, offset %d, len %d\n", tr_torrentName(tor), (int)piece, (int)offset, (int)(walk-buf) ); fprintf( stderr, "%s - Removing %d blocks from cache; %d left\n", MY_NAME, n, tr_ptrArraySize(&cache->blocks) ); #endif err = tr_ioWrite( tor, piece, offset, walk-buf, buf ); tr_free( buf ); ++cache->disk_writes; cache->disk_write_bytes += walk-buf; return err; }
static uint64_t loadFromFile( tr_torrent * tor, uint64_t fieldsToLoad ) { int64_t i; const char * str; uint64_t fieldsLoaded = 0; char * filename; tr_benc top; tr_bool boolVal; const tr_bool wasDirty = tor->isDirty; assert( tr_isTorrent( tor ) ); filename = getResumeFilename( tor ); if( tr_bencLoadFile( &top, TR_FMT_BENC, filename ) ) { tr_tordbg( tor, "Couldn't read \"%s\"", filename ); tr_free( filename ); return fieldsLoaded; } tr_tordbg( tor, "Read resume file \"%s\"", filename ); if( ( fieldsToLoad & TR_FR_CORRUPT ) && tr_bencDictFindInt( &top, KEY_CORRUPT, &i ) ) { tor->corruptPrev = i; fieldsLoaded |= TR_FR_CORRUPT; } if( ( fieldsToLoad & ( TR_FR_PROGRESS | TR_FR_DOWNLOAD_DIR ) ) && ( tr_bencDictFindStr( &top, KEY_DOWNLOAD_DIR, &str ) ) && ( str && *str ) ) { tr_free( tor->downloadDir ); tor->downloadDir = tr_strdup( str ); fieldsLoaded |= TR_FR_DOWNLOAD_DIR; } if( ( fieldsToLoad & ( TR_FR_PROGRESS | TR_FR_INCOMPLETE_DIR ) ) && ( tr_bencDictFindStr( &top, KEY_INCOMPLETE_DIR, &str ) ) && ( str && *str ) ) { tr_free( tor->incompleteDir ); tor->incompleteDir = tr_strdup( str ); fieldsLoaded |= TR_FR_INCOMPLETE_DIR; } if( ( fieldsToLoad & TR_FR_DOWNLOADED ) && tr_bencDictFindInt( &top, KEY_DOWNLOADED, &i ) ) { tor->downloadedPrev = i; fieldsLoaded |= TR_FR_DOWNLOADED; } if( ( fieldsToLoad & TR_FR_UPLOADED ) && tr_bencDictFindInt( &top, KEY_UPLOADED, &i ) ) { tor->uploadedPrev = i; fieldsLoaded |= TR_FR_UPLOADED; } if( ( fieldsToLoad & TR_FR_MAX_PEERS ) && tr_bencDictFindInt( &top, KEY_MAX_PEERS, &i ) ) { tor->maxConnectedPeers = i; fieldsLoaded |= TR_FR_MAX_PEERS; } if( ( fieldsToLoad & TR_FR_RUN ) && tr_bencDictFindBool( &top, KEY_PAUSED, &boolVal ) ) { tor->isRunning = !boolVal; fieldsLoaded |= TR_FR_RUN; } if( ( fieldsToLoad & TR_FR_ADDED_DATE ) && tr_bencDictFindInt( &top, KEY_ADDED_DATE, &i ) ) { tor->addedDate = i; fieldsLoaded |= TR_FR_ADDED_DATE; } if( ( fieldsToLoad & TR_FR_DONE_DATE ) && tr_bencDictFindInt( &top, KEY_DONE_DATE, &i ) ) { tor->doneDate = i; fieldsLoaded |= TR_FR_DONE_DATE; } if( ( fieldsToLoad & TR_FR_ACTIVITY_DATE ) && tr_bencDictFindInt( &top, KEY_ACTIVITY_DATE, &i ) ) { tr_torrentSetActivityDate( tor, i ); fieldsLoaded |= TR_FR_ACTIVITY_DATE; } if( ( fieldsToLoad & TR_FR_TIME_SEEDING ) && tr_bencDictFindInt( &top, KEY_TIME_SEEDING, &i ) ) { tor->secondsSeeding = i; fieldsLoaded |= TR_FR_TIME_SEEDING; } if( ( fieldsToLoad & TR_FR_TIME_DOWNLOADING ) && tr_bencDictFindInt( &top, KEY_TIME_DOWNLOADING, &i ) ) { tor->secondsDownloading = i; fieldsLoaded |= TR_FR_TIME_DOWNLOADING; } if( ( fieldsToLoad & TR_FR_BANDWIDTH_PRIORITY ) && tr_bencDictFindInt( &top, KEY_BANDWIDTH_PRIORITY, &i ) && tr_isPriority( i ) ) { tr_torrentSetPriority( tor, i ); fieldsLoaded |= TR_FR_BANDWIDTH_PRIORITY; } if( fieldsToLoad & TR_FR_PEERS ) fieldsLoaded |= loadPeers( &top, tor ); if( fieldsToLoad & TR_FR_FILE_PRIORITIES ) fieldsLoaded |= loadFilePriorities( &top, tor ); if( fieldsToLoad & TR_FR_PROGRESS ) fieldsLoaded |= loadProgress( &top, tor ); if( fieldsToLoad & TR_FR_DND ) fieldsLoaded |= loadDND( &top, tor ); if( fieldsToLoad & TR_FR_SPEEDLIMIT ) fieldsLoaded |= loadSpeedLimits( &top, tor ); if( fieldsToLoad & TR_FR_RATIOLIMIT ) fieldsLoaded |= loadRatioLimits( &top, tor ); if( fieldsToLoad & TR_FR_IDLELIMIT ) fieldsLoaded |= loadIdleLimits( &top, tor ); /* loading the resume file triggers of a lot of changes, * but none of them needs to trigger a re-saving of the * same resume information... */ tor->isDirty = wasDirty; tr_bencFree( &top ); tr_free( filename ); return fieldsLoaded; }
static uint64_t loadProgress( tr_benc * dict, tr_torrent * tor ) { size_t i, n; uint64_t ret = 0; tr_benc * prog; const tr_info * inf = tr_torrentInfo( tor ); for( i=0, n=inf->pieceCount; i<n; ++i ) inf->pieces[i].timeChecked = 0; if( tr_bencDictFindDict( dict, KEY_PROGRESS, &prog ) ) { const char * err; const char * str; const uint8_t * raw; size_t rawlen; tr_benc * l; tr_benc * b; struct tr_bitset bitset = TR_BITSET_INIT; if( tr_bencDictFindList( prog, KEY_PROGRESS_CHECKTIME, &l ) ) { /* per-piece timestamps were added in 2.20. If some of a file's pieces have been checked more recently than the file's mtime, and some lest recently, then that file will have a list containing timestamps for each piece. However, the most common use case is that the file doesn't change after it's downloaded. To reduce overhead in the .resume file, only a single timestamp is saved for the file if *all* or *none* of the pieces were tested more recently than the file's mtime. */ tr_file_index_t fi; for( fi=0; fi<inf->fileCount; ++fi ) { tr_benc * b = tr_bencListChild( l, fi ); const tr_file * f = &inf->files[fi]; tr_piece * p = &inf->pieces[f->firstPiece]; const tr_piece * pend = &inf->pieces[f->lastPiece]+1; if( tr_bencIsInt( b ) ) { int64_t t; tr_bencGetInt( b, &t ); for( ; p!=pend; ++p ) p->timeChecked = (time_t)t; } else if( tr_bencIsList( b ) ) { int i = 0; int64_t offset = 0; const int pieces = f->lastPiece + 1 - f->firstPiece; tr_bencGetInt( tr_bencListChild( b, 0 ), &offset ); for( i=0; i<pieces; ++i ) { int64_t t = 0; tr_bencGetInt( tr_bencListChild( b, i+1 ), &t ); inf->pieces[f->firstPiece+i].timeChecked = (time_t)(t ? t + offset : 0); } } } } else if( tr_bencDictFindList( prog, KEY_PROGRESS_MTIMES, &l ) ) { tr_file_index_t fi; /* Before 2.20, we stored the files' mtimes in the .resume file. When loading the .resume file, a torrent's file would be flagged as untested if its stored mtime didn't match its real mtime. */ for( fi=0; fi<inf->fileCount; ++fi ) { int64_t t; if( tr_bencGetInt( tr_bencListChild( l, fi ), &t ) ) { const tr_file * f = &inf->files[fi]; tr_piece * p = &inf->pieces[f->firstPiece]; const tr_piece * pend = &inf->pieces[f->lastPiece]; const time_t mtime = tr_torrentGetFileMTime( tor, fi ); const time_t timeChecked = mtime==t ? mtime : 0; for( ; p!=pend; ++p ) p->timeChecked = timeChecked; } } } err = NULL; if(( b = tr_bencDictFind( prog, KEY_PROGRESS_BLOCKS ))) { if( !tr_bitsetFromBenc( &bitset, b ) ) err = "Invalid value for PIECES"; } else if( tr_bencDictFindStr( prog, KEY_PROGRESS_HAVE, &str ) ) { if( !strcmp( str, "all" ) ) tr_bitsetSetHaveAll( &bitset ); else err = "Invalid value for HAVE"; } else if( tr_bencDictFindRaw( prog, KEY_PROGRESS_BITFIELD, &raw, &rawlen ) ) { bitset.bitfield.bits = (void*) raw; bitset.bitfield.byteCount = rawlen; bitset.bitfield.bitCount = rawlen * 8; } else err = "Couldn't find 'pieces' or 'have' or 'bitfield'"; if( !err && !tr_cpBlockBitsetInit( &tor->completion, &bitset ) ) err = "Error loading bitfield"; if( err != NULL ) tr_tordbg( tor, "Torrent needs to be verified - %s", err ); ret = TR_FR_PROGRESS; } return ret; }
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; }
/* returns 0 on success, or an errno on failure */ static int readOrWriteBytes( tr_session * session, tr_torrent * tor, int ioMode, tr_file_index_t fileIndex, uint64_t fileOffset, void * buf, size_t buflen ) { const tr_info * info = &tor->info; const tr_file * file = &info->files[fileIndex]; int fd = -1; int err = 0; const tr_bool doWrite = ioMode >= TR_IO_WRITE; //if( doWrite ) // fprintf( stderr, "in file %s at offset %zu, writing %zu bytes; file length is %zu\n", file->name, (size_t)fileOffset, buflen, (size_t)file->length ); assert( fileIndex < info->fileCount ); assert( !file->length || ( fileOffset < file->length ) ); assert( fileOffset + buflen <= file->length ); if( !file->length ) return 0; fd = tr_fdFileGetCached( session, tr_torrentId( tor ), fileIndex, doWrite ); if( fd < 0 ) { /* the fd cache doesn't have this file... * we'll need to open it and maybe create it */ char * subpath; const char * base; tr_bool fileExists; tr_preallocation_mode preallocationMode; fileExists = tr_torrentFindFile2( tor, fileIndex, &base, &subpath ); if( !fileExists ) { base = tr_torrentGetCurrentDir( tor ); if( tr_sessionIsIncompleteFileNamingEnabled( tor->session ) ) subpath = tr_torrentBuildPartial( tor, fileIndex ); else subpath = tr_strdup( file->name ); } if( ( file->dnd ) || ( ioMode < TR_IO_WRITE ) ) preallocationMode = TR_PREALLOCATE_NONE; else preallocationMode = tor->session->preallocationMode; if( ( ioMode < TR_IO_WRITE ) && !fileExists ) /* does file exist? */ { err = ENOENT; } else { char * filename = tr_buildPath( base, subpath, NULL ); if( ( fd = tr_fdFileCheckout( session, tor->uniqueId, fileIndex, filename, doWrite, preallocationMode, file->length ) ) < 0 ) { err = errno; tr_torerr( tor, "tr_fdFileCheckout failed for \"%s\": %s", filename, tr_strerror( err ) ); } tr_free( filename ); } if( doWrite && !err ) tr_statsFileCreated( tor->session ); tr_free( subpath ); } if( !err ) { /* check & see if someone deleted the file while it was in our cache */ struct stat sb; const tr_bool file_disappeared = fstat( fd, &sb ) || sb.st_nlink < 1; if( file_disappeared ) { tr_torrentSetLocalError( tor, "Please Verify Local Data! A file disappeared: \"%s\"", tor->info.files[fileIndex].name ); err = ENOENT; } if( ioMode == TR_IO_READ ) { const int rc = tr_pread( fd, buf, buflen, fileOffset ); if( rc < 0 ) { err = errno; tr_torerr( tor, "read failed for \"%s\": %s", file->name, tr_strerror( err ) ); } } else if( ioMode == TR_IO_PREFETCH ) { const int rc = tr_prefetch( fd, fileOffset, buflen ); if( rc < 0 ) { /* (don't set "err" here... it's okay for prefetch to fail) */ tr_tordbg( tor, "prefetch failed for \"%s\": %s", file->name, tr_strerror( errno ) ); } } else if( ioMode == TR_IO_WRITE ) { const int rc = tr_pwrite( fd, buf, buflen, fileOffset ); if( rc < 0 ) { err = errno; tr_torerr( tor, "write failed for \"%s\": %s", file->name, tr_strerror( err ) ); } } else { abort(); } } return err; }
static uint64_t parseVersion1( tr_torrent * tor, const uint8_t * buf, const uint8_t * end, uint64_t fieldsToLoad ) { uint64_t ret = 0; while( end - buf >= 5 ) { uint8_t id; uint32_t len; readBytes( &id, &buf, sizeof( id ) ); readBytes( &len, &buf, sizeof( len ) ); if( buf + len > end ) { tr_torerr( tor, "Resume file seems to be corrupt. Skipping." ); } else if( fieldsToLoad & internalIdToPublicBitfield( id ) ) switch( id ) { case FR_ID_DOWNLOADED: ret |= parseDownloaded( tor, buf, len ); break; case FR_ID_UPLOADED: ret |= parseUploaded( tor, buf, len ); break; case FR_ID_PROGRESS: ret |= parseProgress( tor, buf, len ); break; case FR_ID_PRIORITY: ret |= parsePriorities( tor, buf, len ); break; case FR_ID_SPEED: ret |= parseSpeedLimit( tor, buf, len ); break; case FR_ID_RUN: ret |= parseRun( tor, buf, len ); break; case FR_ID_CORRUPT: ret |= parseCorrupt( tor, buf, len ); break; case FR_ID_PEERS: ret |= parsePeers( tor, buf, len ); break; case FR_ID_MAX_PEERS: ret |= parseConnections( tor, buf, len ); break; case FR_ID_DOWNLOAD_DIR: ret |= parseDownloadDir( tor, buf, len ); break; default: tr_tordbg( tor, "Skipping unknown resume code %d", (int)id ); break; } buf += len; } return ret; }
static uint64_t loadFromFile( tr_torrent * tor, uint64_t fieldsToLoad ) { int64_t i; const char * str; uint64_t fieldsLoaded = 0; char * filename; tr_benc top; filename = getResumeFilename( tor ); if( tr_bencLoadFile( filename, &top ) ) { tr_tordbg( tor, "Couldn't read \"%s\"; trying old format.", filename ); fieldsLoaded = tr_fastResumeLoad( tor, fieldsToLoad ); if( ( fieldsLoaded != 0 ) && ( fieldsToLoad == ~(uint64_t)0 ) ) { tr_torrentSaveResume( tor ); tr_fastResumeRemove( tor ); tr_tordbg( tor, "Migrated resume file to \"%s\"", filename ); } tr_free( filename ); return fieldsLoaded; } tr_tordbg( tor, "Read resume file \"%s\"", filename ); if( ( fieldsToLoad & TR_FR_CORRUPT ) && tr_bencDictFindInt( &top, KEY_CORRUPT, &i ) ) { tor->corruptPrev = i; fieldsLoaded |= TR_FR_CORRUPT; } if( ( fieldsToLoad & ( TR_FR_PROGRESS | TR_FR_DOWNLOAD_DIR ) ) && tr_bencDictFindStr( &top, KEY_DOWNLOAD_DIR, &str ) ) { tr_free( tor->downloadDir ); tor->downloadDir = tr_strdup( str ); fieldsLoaded |= TR_FR_DOWNLOAD_DIR; } if( ( fieldsToLoad & TR_FR_DOWNLOADED ) && tr_bencDictFindInt( &top, KEY_DOWNLOADED, &i ) ) { tor->downloadedPrev = i; fieldsLoaded |= TR_FR_DOWNLOADED; } if( ( fieldsToLoad & TR_FR_UPLOADED ) && tr_bencDictFindInt( &top, KEY_UPLOADED, &i ) ) { tor->uploadedPrev = i; fieldsLoaded |= TR_FR_UPLOADED; } if( ( fieldsToLoad & TR_FR_MAX_PEERS ) && tr_bencDictFindInt( &top, KEY_MAX_PEERS, &i ) ) { tor->maxConnectedPeers = i; fieldsLoaded |= TR_FR_MAX_PEERS; } if( ( fieldsToLoad & TR_FR_RUN ) && tr_bencDictFindInt( &top, KEY_PAUSED, &i ) ) { tor->isRunning = i ? 0 : 1; fieldsLoaded |= TR_FR_RUN; } if( ( fieldsToLoad & TR_FR_ADDED_DATE ) && tr_bencDictFindInt( &top, KEY_ADDED_DATE, &i ) ) { tor->addedDate = i; fieldsLoaded |= TR_FR_ADDED_DATE; } if( ( fieldsToLoad & TR_FR_DONE_DATE ) && tr_bencDictFindInt( &top, KEY_DONE_DATE, &i ) ) { tor->doneDate = i; fieldsLoaded |= TR_FR_DONE_DATE; } if( ( fieldsToLoad & TR_FR_ACTIVITY_DATE ) && tr_bencDictFindInt( &top, KEY_ACTIVITY_DATE, &i ) ) { tor->activityDate = i; fieldsLoaded |= TR_FR_ACTIVITY_DATE; } if( fieldsToLoad & TR_FR_PEERS ) fieldsLoaded |= loadPeers( &top, tor ); if( fieldsToLoad & TR_FR_PRIORITY ) fieldsLoaded |= loadPriorities( &top, tor ); if( fieldsToLoad & TR_FR_PROGRESS ) fieldsLoaded |= loadProgress( &top, tor ); if( fieldsToLoad & TR_FR_DND ) fieldsLoaded |= loadDND( &top, tor ); if( fieldsToLoad & TR_FR_SPEEDLIMIT ) fieldsLoaded |= loadSpeedLimits( &top, tor ); tr_bencFree( &top ); tr_free( filename ); return fieldsLoaded; }
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; }