1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2025-02-24 23:12:35 +00:00
transmission/libtransmission/resume.c
Jordan Lee 4230cd7786 (trunk libT) don't cache unininteresting peer addresses between sessions
For example, if we're both seeds, or if the peer is not connectible, don't bother caching it for the next session. If it's still alive, we'll find it up through DHT or tracker announces next time around. As with r12253, this commit's intention is to reduce the number of unproductive peer_atoms that we waste time trying to connect to.
2011-03-29 15:23:54 +00:00

900 lines
27 KiB
C

/*
* This file Copyright (C) Mnemosyne LLC
*
* 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$
*/
#include <unistd.h> /* unlink */
#include <string.h>
#include "transmission.h"
#include "bencode.h"
#include "completion.h"
#include "metainfo.h" /* tr_metainfoGetBasename() */
#include "peer-mgr.h" /* pex */
#include "platform.h" /* tr_getResumeDir() */
#include "resume.h"
#include "session.h"
#include "torrent.h"
#include "utils.h" /* tr_buildPath */
#define KEY_ACTIVITY_DATE "activity-date"
#define KEY_ADDED_DATE "added-date"
#define KEY_CORRUPT "corrupt"
#define KEY_DONE_DATE "done-date"
#define KEY_DOWNLOAD_DIR "destination"
#define KEY_DND "dnd"
#define KEY_DOWNLOADED "downloaded"
#define KEY_INCOMPLETE_DIR "incomplete-dir"
#define KEY_MAX_PEERS "max-peers"
#define KEY_PAUSED "paused"
#define KEY_PEERS "peers2"
#define KEY_PEERS6 "peers2-6"
#define KEY_FILE_PRIORITIES "priority"
#define KEY_BANDWIDTH_PRIORITY "bandwidth-priority"
#define KEY_PROGRESS "progress"
#define KEY_SPEEDLIMIT_OLD "speed-limit"
#define KEY_SPEEDLIMIT_UP "speed-limit-up"
#define KEY_SPEEDLIMIT_DOWN "speed-limit-down"
#define KEY_RATIOLIMIT "ratio-limit"
#define KEY_IDLELIMIT "idle-limit"
#define KEY_UPLOADED "uploaded"
#define KEY_SPEED_KiBps "speed"
#define KEY_SPEED_Bps "speed-Bps"
#define KEY_USE_GLOBAL_SPEED_LIMIT "use-global-speed-limit"
#define KEY_USE_SPEED_LIMIT "use-speed-limit"
#define KEY_TIME_SEEDING "seeding-time-seconds"
#define KEY_TIME_DOWNLOADING "downloading-time-seconds"
#define KEY_SPEEDLIMIT_DOWN_SPEED "down-speed"
#define KEY_SPEEDLIMIT_DOWN_MODE "down-mode"
#define KEY_SPEEDLIMIT_UP_SPEED "up-speed"
#define KEY_SPEEDLIMIT_UP_MODE "up-mode"
#define KEY_RATIOLIMIT_RATIO "ratio-limit"
#define KEY_RATIOLIMIT_MODE "ratio-mode"
#define KEY_IDLELIMIT_MINS "idle-limit"
#define KEY_IDLELIMIT_MODE "idle-mode"
#define KEY_PROGRESS_CHECKTIME "time-checked"
#define KEY_PROGRESS_MTIMES "mtimes"
#define KEY_PROGRESS_BITFIELD "bitfield"
#define KEY_PROGRESS_BLOCKS "blocks"
#define KEY_PROGRESS_HAVE "have"
enum
{
MAX_REMEMBERED_PEERS = 200
};
static char*
getResumeFilename( const tr_torrent * tor )
{
char * base = tr_metainfoGetBasename( tr_torrentInfo( tor ) );
char * filename = tr_strdup_printf( "%s" TR_PATH_DELIMITER_STR "%s.resume",
tr_getResumeDir( tor->session ), base );
tr_free( base );
return filename;
}
/***
****
***/
static void
savePeers( tr_benc * dict, const tr_torrent * tor )
{
int count;
tr_pex * pex;
count = tr_peerMgrGetPeers( (tr_torrent*) tor, &pex, TR_AF_INET, TR_PEERS_INTERESTING, MAX_REMEMBERED_PEERS );
if( count > 0 )
tr_bencDictAddRaw( dict, KEY_PEERS, pex, sizeof( tr_pex ) * count );
tr_free( pex );
count = tr_peerMgrGetPeers( (tr_torrent*) tor, &pex, TR_AF_INET6, TR_PEERS_INTERESTING, MAX_REMEMBERED_PEERS );
if( count > 0 )
tr_bencDictAddRaw( dict, KEY_PEERS6, pex, sizeof( tr_pex ) * count );
tr_free( pex );
}
static int
addPeers( tr_torrent * tor, const uint8_t * buf, int buflen )
{
int i;
int numAdded = 0;
const int count = buflen / sizeof( tr_pex );
for( i=0; i<count && numAdded<MAX_REMEMBERED_PEERS; ++i )
{
tr_pex pex;
memcpy( &pex, buf + ( i * sizeof( tr_pex ) ), sizeof( tr_pex ) );
if( tr_isPex( &pex ) )
{
tr_peerMgrAddPex( tor, TR_PEER_FROM_RESUME, &pex, -1 );
++numAdded;
}
}
return numAdded;
}
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 void
saveDND( tr_benc * dict,
const tr_torrent * tor )
{
const tr_info * inf = tr_torrentInfo( tor );
const tr_file_index_t n = inf->fileCount;
tr_file_index_t i;
tr_benc * list;
list = tr_bencDictAddList( dict, KEY_DND, n );
for( i = 0; i < n; ++i )
tr_bencListAddInt( list, inf->files[i].dnd ? 1 : 0 );
}
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 void
saveFilePriorities( tr_benc * dict, const tr_torrent * tor )
{
const tr_info * inf = tr_torrentInfo( tor );
const tr_file_index_t n = inf->fileCount;
tr_file_index_t i;
tr_benc * list;
list = tr_bencDictAddList( dict, KEY_FILE_PRIORITIES, n );
for( i = 0; i < n; ++i )
tr_bencListAddInt( list, inf->files[i].priority );
}
static uint64_t
loadFilePriorities( 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;
if( tr_bencDictFindList( dict, KEY_FILE_PRIORITIES, &list )
&& ( tr_bencListSize( list ) == n ) )
{
int64_t priority;
tr_file_index_t i;
for( i = 0; i < n; ++i )
if( tr_bencGetInt( tr_bencListChild( list, i ), &priority ) )
tr_torrentInitFilePriority( tor, i, priority );
ret = TR_FR_FILE_PRIORITIES;
}
return ret;
}
/***
****
***/
static void
saveSingleSpeedLimit( tr_benc * d, const tr_torrent * tor, tr_direction dir )
{
tr_bencDictReserve( d, 3 );
tr_bencDictAddInt( d, KEY_SPEED_Bps, tr_torrentGetSpeedLimit_Bps( tor, dir ) );
tr_bencDictAddBool( d, KEY_USE_GLOBAL_SPEED_LIMIT, tr_torrentUsesSessionLimits( tor ) );
tr_bencDictAddBool( d, KEY_USE_SPEED_LIMIT, tr_torrentUsesSpeedLimit( tor, dir ) );
}
static void
saveSpeedLimits( tr_benc * dict, const tr_torrent * tor )
{
saveSingleSpeedLimit( tr_bencDictAddDict( dict, KEY_SPEEDLIMIT_DOWN, 0 ), tor, TR_DOWN );
saveSingleSpeedLimit( tr_bencDictAddDict( dict, KEY_SPEEDLIMIT_UP, 0 ), tor, TR_UP );
}
static void
saveRatioLimits( tr_benc * dict, const tr_torrent * tor )
{
tr_benc * d = tr_bencDictAddDict( dict, KEY_RATIOLIMIT, 2 );
tr_bencDictAddReal( d, KEY_RATIOLIMIT_RATIO, tr_torrentGetRatioLimit( tor ) );
tr_bencDictAddInt( d, KEY_RATIOLIMIT_MODE, tr_torrentGetRatioMode( tor ) );
}
static void
saveIdleLimits( tr_benc * dict, const tr_torrent * tor )
{
tr_benc * d = tr_bencDictAddDict( dict, KEY_IDLELIMIT, 2 );
tr_bencDictAddInt( d, KEY_IDLELIMIT_MINS, tr_torrentGetIdleLimit( tor ) );
tr_bencDictAddInt( d, KEY_IDLELIMIT_MODE, tr_torrentGetIdleMode( tor ) );
}
static void
loadSingleSpeedLimit( tr_benc * d, tr_direction dir, tr_torrent * tor )
{
int64_t i;
bool boolVal;
if( tr_bencDictFindInt( d, KEY_SPEED_Bps, &i ) )
tr_torrentSetSpeedLimit_Bps( tor, dir, i );
else if( tr_bencDictFindInt( d, KEY_SPEED_KiBps, &i ) )
tr_torrentSetSpeedLimit_Bps( tor, dir, i*1024 );
if( tr_bencDictFindBool( d, KEY_USE_SPEED_LIMIT, &boolVal ) )
tr_torrentUseSpeedLimit( tor, dir, boolVal );
if( tr_bencDictFindBool( d, KEY_USE_GLOBAL_SPEED_LIMIT, &boolVal ) )
tr_torrentUseSessionLimits( tor, boolVal );
}
enum old_speed_modes
{
TR_SPEEDLIMIT_GLOBAL, /* only follow the overall speed limit */
TR_SPEEDLIMIT_SINGLE /* only follow the per-torrent limit */
};
static uint64_t
loadSpeedLimits( tr_benc * dict, tr_torrent * tor )
{
uint64_t ret = 0;
tr_benc * d;
if( tr_bencDictFindDict( dict, KEY_SPEEDLIMIT_UP, &d ) )
{
loadSingleSpeedLimit( d, TR_UP, tor );
ret = TR_FR_SPEEDLIMIT;
}
if( tr_bencDictFindDict( dict, KEY_SPEEDLIMIT_DOWN, &d ) )
{
loadSingleSpeedLimit( d, TR_DOWN, tor );
ret = TR_FR_SPEEDLIMIT;
}
/* older speedlimit structure */
if( !ret && tr_bencDictFindDict( dict, KEY_SPEEDLIMIT_OLD, &d ) )
{
int64_t i;
if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_DOWN_SPEED, &i ) )
tr_torrentSetSpeedLimit_Bps( tor, TR_DOWN, i*1024 );
if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_DOWN_MODE, &i ) ) {
tr_torrentUseSpeedLimit( tor, TR_DOWN, i==TR_SPEEDLIMIT_SINGLE );
tr_torrentUseSessionLimits( tor, i==TR_SPEEDLIMIT_GLOBAL );
}
if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_UP_SPEED, &i ) )
tr_torrentSetSpeedLimit_Bps( tor, TR_UP, i*1024 );
if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_UP_MODE, &i ) ) {
tr_torrentUseSpeedLimit( tor, TR_UP, i==TR_SPEEDLIMIT_SINGLE );
tr_torrentUseSessionLimits( tor, i==TR_SPEEDLIMIT_GLOBAL );
}
ret = TR_FR_SPEEDLIMIT;
}
return ret;
}
static uint64_t
loadRatioLimits( tr_benc * dict,
tr_torrent * tor )
{
uint64_t ret = 0;
tr_benc * d;
if( tr_bencDictFindDict( dict, KEY_RATIOLIMIT, &d ) )
{
int64_t i;
double dratio;
if( tr_bencDictFindReal( d, KEY_RATIOLIMIT_RATIO, &dratio ) )
tr_torrentSetRatioLimit( tor, dratio );
if( tr_bencDictFindInt( d, KEY_RATIOLIMIT_MODE, &i ) )
tr_torrentSetRatioMode( tor, i );
ret = TR_FR_RATIOLIMIT;
}
return ret;
}
static uint64_t
loadIdleLimits( tr_benc * dict,
tr_torrent * tor )
{
uint64_t ret = 0;
tr_benc * d;
if( tr_bencDictFindDict( dict, KEY_IDLELIMIT, &d ) )
{
int64_t i;
int64_t imin;
if( tr_bencDictFindInt( d, KEY_IDLELIMIT_MINS, &imin ) )
tr_torrentSetIdleLimit( tor, imin );
if( tr_bencDictFindInt( d, KEY_IDLELIMIT_MODE, &i ) )
tr_torrentSetIdleMode( tor, i );
ret = TR_FR_IDLELIMIT;
}
return ret;
}
/***
****
***/
static void
bitfieldToBenc( const tr_bitfield * b, tr_benc * benc )
{
if( tr_bitfieldHasAll( b ) )
tr_bencInitStr( benc, "all", 3 );
else if( tr_bitfieldHasNone( b ) )
tr_bencInitStr( benc, "none", 4 );
else
tr_bencInitRaw( benc, b->bits, b->byte_count );
}
static void
saveProgress( tr_benc * dict, tr_torrent * tor )
{
tr_benc * l;
tr_benc * prog;
tr_file_index_t fi;
const tr_info * inf = tr_torrentInfo( tor );
const time_t now = tr_time( );
prog = tr_bencDictAddDict( dict, KEY_PROGRESS, 3 );
/* add the file/piece check timestamps... */
l = tr_bencDictAddList( prog, KEY_PROGRESS_CHECKTIME, inf->fileCount );
for( fi=0; fi<inf->fileCount; ++fi )
{
const tr_piece * p;
const tr_piece * pend;
time_t oldest_nonzero = now;
time_t newest = 0;
bool has_zero = false;
const time_t mtime = tr_torrentGetFileMTime( tor, fi );
const tr_file * f = &inf->files[fi];
/* get the oldest and newest nonzero timestamps for pieces in this file */
for( p=&inf->pieces[f->firstPiece], pend=&inf->pieces[f->lastPiece]; p!=pend; ++p )
{
if( !p->timeChecked )
has_zero = true;
else if( oldest_nonzero > p->timeChecked )
oldest_nonzero = p->timeChecked;
if( newest < p->timeChecked )
newest = p->timeChecked;
}
/* 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. */
if( !has_zero && ( mtime <= oldest_nonzero ) ) /* all checked */
tr_bencListAddInt( l, oldest_nonzero );
else if( newest < mtime ) /* none checked */
tr_bencListAddInt( l, newest );
else { /* some are checked, some aren't... so list piece by piece */
const int offset = oldest_nonzero - 1;
tr_benc * ll = tr_bencListAddList( l, 2 + f->lastPiece - f->firstPiece );
tr_bencListAddInt( ll, offset );
for( p=&inf->pieces[f->firstPiece], pend=&inf->pieces[f->lastPiece]+1; p!=pend; ++p )
tr_bencListAddInt( ll, p->timeChecked ? p->timeChecked - offset : 0 );
}
}
/* add the progress */
if( tor->completeness == TR_SEED )
tr_bencDictAddStr( prog, KEY_PROGRESS_HAVE, "all" );
/* add the blocks bitfield */
bitfieldToBenc( &tor->completion.blockBitfield,
tr_bencDictAdd( prog, KEY_PROGRESS_BLOCKS ) );
}
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_bitfield blocks = TR_BITFIELD_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;
tr_bitfieldConstruct( &blocks, tor->blockCount );
if(( b = tr_bencDictFind( prog, KEY_PROGRESS_BLOCKS )))
{
size_t buflen;
const uint8_t * buf;
if( !tr_bencGetRaw( b, &buf, &buflen ) )
err = "Invalid value for \"blocks\"";
else if( ( buflen == 3 ) && !memcmp( buf, "all", 3 ) )
tr_bitfieldSetHasAll( &blocks );
else if( ( buflen == 4 ) && !memcmp( buf, "none", 4 ) )
tr_bitfieldSetHasNone( &blocks );
else
tr_bitfieldSetRaw( &blocks, buf, buflen );
}
else if( tr_bencDictFindStr( prog, KEY_PROGRESS_HAVE, &str ) )
{
if( !strcmp( str, "all" ) )
tr_bitfieldSetHasAll( &blocks );
else
err = "Invalid value for HAVE";
}
else if( tr_bencDictFindRaw( prog, KEY_PROGRESS_BITFIELD, &raw, &rawlen ) )
{
tr_bitfieldSetRaw( &blocks, raw, rawlen );
}
else err = "Couldn't find 'pieces' or 'have' or 'bitfield'";
if( !err && !tr_cpBlockInit( &tor->completion, &blocks ) )
err = "Error loading bitfield";
if( err != NULL )
tr_tordbg( tor, "Torrent needs to be verified - %s", err );
tr_bitfieldDestruct( &blocks );
ret = TR_FR_PROGRESS;
}
return ret;
}
/***
****
***/
void
tr_torrentSaveResume( tr_torrent * tor )
{
int err;
tr_benc top;
char * filename;
if( !tr_isTorrent( tor ) )
return;
tr_bencInitDict( &top, 50 ); /* arbitrary "big enough" number */
tr_bencDictAddInt( &top, KEY_TIME_SEEDING, tor->secondsSeeding );
tr_bencDictAddInt( &top, KEY_TIME_DOWNLOADING, tor->secondsDownloading );
tr_bencDictAddInt( &top, KEY_ACTIVITY_DATE, tor->activityDate );
tr_bencDictAddInt( &top, KEY_ADDED_DATE, tor->addedDate );
tr_bencDictAddInt( &top, KEY_CORRUPT, tor->corruptPrev + tor->corruptCur );
tr_bencDictAddInt( &top, KEY_DONE_DATE, tor->doneDate );
tr_bencDictAddStr( &top, KEY_DOWNLOAD_DIR, tor->downloadDir );
if( tor->incompleteDir != NULL )
tr_bencDictAddStr( &top, KEY_INCOMPLETE_DIR, tor->incompleteDir );
tr_bencDictAddInt( &top, KEY_DOWNLOADED, tor->downloadedPrev + tor->downloadedCur );
tr_bencDictAddInt( &top, KEY_UPLOADED, tor->uploadedPrev + tor->uploadedCur );
tr_bencDictAddInt( &top, KEY_MAX_PEERS, tor->maxConnectedPeers );
tr_bencDictAddInt( &top, KEY_BANDWIDTH_PRIORITY, tr_torrentGetPriority( tor ) );
tr_bencDictAddBool( &top, KEY_PAUSED, !tor->isRunning );
savePeers( &top, tor );
if( tr_torrentHasMetadata( tor ) )
{
saveFilePriorities( &top, tor );
saveDND( &top, tor );
saveProgress( &top, tor );
}
saveSpeedLimits( &top, tor );
saveRatioLimits( &top, tor );
saveIdleLimits( &top, tor );
filename = getResumeFilename( tor );
if(( err = tr_bencToFile( &top, TR_FMT_BENC, filename )))
tr_torrentSetLocalError( tor, "Unable to save resume file: %s", tr_strerror( err ) );
tr_free( filename );
tr_bencFree( &top );
}
static uint64_t
loadFromFile( tr_torrent * tor,
uint64_t fieldsToLoad )
{
int64_t i;
const char * str;
char * filename;
tr_benc top;
bool boolVal;
uint64_t fieldsLoaded = 0;
const 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
setFromCtor( tr_torrent * tor,
uint64_t fields,
const tr_ctor * ctor,
int mode )
{
uint64_t ret = 0;
if( fields & TR_FR_RUN )
{
bool isPaused;
if( !tr_ctorGetPaused( ctor, mode, &isPaused ) )
{
tor->isRunning = !isPaused;
ret |= TR_FR_RUN;
}
}
if( fields & TR_FR_MAX_PEERS )
if( !tr_ctorGetPeerLimit( ctor, mode, &tor->maxConnectedPeers ) )
ret |= TR_FR_MAX_PEERS;
if( fields & TR_FR_DOWNLOAD_DIR )
{
const char * path;
if( !tr_ctorGetDownloadDir( ctor, mode, &path ) && path && *path )
{
ret |= TR_FR_DOWNLOAD_DIR;
tr_free( tor->downloadDir );
tor->downloadDir = tr_strdup( path );
}
}
return ret;
}
static uint64_t
useManditoryFields( tr_torrent * tor,
uint64_t fields,
const tr_ctor * ctor )
{
return setFromCtor( tor, fields, ctor, TR_FORCE );
}
static uint64_t
useFallbackFields( tr_torrent * tor,
uint64_t fields,
const tr_ctor * ctor )
{
return setFromCtor( tor, fields, ctor, TR_FALLBACK );
}
uint64_t
tr_torrentLoadResume( tr_torrent * tor,
uint64_t fieldsToLoad,
const tr_ctor * ctor )
{
uint64_t ret = 0;
assert( tr_isTorrent( tor ) );
ret |= useManditoryFields( tor, fieldsToLoad, ctor );
fieldsToLoad &= ~ret;
ret |= loadFromFile( tor, fieldsToLoad );
fieldsToLoad &= ~ret;
ret |= useFallbackFields( tor, fieldsToLoad, ctor );
return ret;
}
void
tr_torrentRemoveResume( const tr_torrent * tor )
{
char * filename = getResumeFilename( tor );
unlink( filename );
tr_free( filename );
}