source: trunk/libtransmission/resume.c @ 5608

Last change on this file since 5608 was 5608, checked in by charles, 14 years ago

#394: resume files should use original name instead of hash name

File size: 11.3 KB
Line 
1/*
2 * This file Copyright (C) 2008 Charles Kerr <charles@rebelbase.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id:$
11 */
12
13#include <sys/types.h> /* stat */
14#include <sys/stat.h> /* stat */
15#include <unistd.h> /* unlink, stat */
16
17#include <string.h>
18
19#include "transmission.h"
20#include "bencode.h"
21#include "completion.h"
22#include "fastresume.h"
23#include "peer-mgr.h" /* pex */
24#include "platform.h" /* tr_getResumeDir */
25#include "resume.h"
26#include "torrent.h"
27#include "utils.h" /* tr_buildPath */
28
29#define KEY_CORRUPT     "corrupt"
30#define KEY_DESTINATION "destination"
31#define KEY_DOWNLOADED  "downloaded"
32#define KEY_MAX_PEERS   "max-peers"
33#define KEY_PAUSED      "paused"
34#define KEY_PEERS       "peers"
35#define KEY_PRIORITY    "priority"
36#define KEY_PROGRESS    "progress"
37#define KEY_SPEEDLIMIT  "speed-limit"
38#define KEY_UPLOADED    "uploaded"
39
40#define KEY_SPEEDLIMIT_DOWN_SPEED "down-speed"
41#define KEY_SPEEDLIMIT_DOWN_MODE  "down-mode"
42#define KEY_SPEEDLIMIT_UP_SPEED   "up-speed"
43#define KEY_SPEEDLIMIT_UP_MODE    "up-mode"
44
45#define KEY_PROGRESS_MTIMES "mtimes"
46#define KEY_PROGRESS_BITFIELD "bitfield"
47
48/***
49****
50***/
51
52static time_t*
53getMTimes( const tr_torrent * tor, int * setme_n )
54{
55    int i;
56    const int n = tor->info.fileCount;
57    time_t * m = tr_new( time_t, n );
58
59    for( i=0; i<n; ++i ) {
60        char fname[MAX_PATH_LENGTH];
61        struct stat sb;
62        tr_buildPath( fname, sizeof(fname),
63                      tor->destination, tor->info.files[i].name, NULL );
64        if ( !stat( fname, &sb ) && S_ISREG( sb.st_mode ) ) {
65#ifdef SYS_DARWIN
66            m[i] = sb.st_mtimespec.tv_sec;
67#else
68            m[i] = sb.st_mtime;
69#endif
70        }
71    }
72
73    *setme_n = n;
74    return m;
75}
76
77static void
78getResumeFilename( char * buf, size_t buflen, const tr_torrent * tor )
79{
80    const char * dir = tr_getResumeDir( tor->handle );
81    char base[4096];
82    snprintf( base, sizeof( base ), "%s.%16.16s.resume", tor->info.name, tor->info.hashString );
83    tr_buildPath( buf, buflen, dir, base, NULL );
84}
85
86/***
87****
88***/
89
90static void
91savePeers( tr_benc * dict, const tr_torrent * tor )
92{
93    tr_pex * pex;
94    const int count = tr_peerMgrGetPeers( tor->handle->peerMgr, tor->info.hash, &pex );
95    if( count > 0 ) {
96        tr_benc * child = tr_bencDictAdd( dict, KEY_PEERS );
97        tr_bencInitStrDupLen( child, (const char*)pex, sizeof(tr_pex)*count );
98        tr_free( pex );
99    }
100}
101
102static uint64_t
103loadPeers( tr_benc * dict, tr_torrent * tor )
104{
105    uint64_t ret = 0;
106    tr_benc * p;
107
108    if(( p = tr_bencDictFindType( dict, KEY_PEERS, TYPE_STR )))
109    {
110        int i;
111        const char * str = p->val.s.s;
112        const size_t len = p->val.s.i;
113        const int count = len / sizeof( tr_pex );
114        for( i=0; i<count; ++i ) {
115            tr_pex pex;
116            memcpy( &pex, str + (i*sizeof(tr_pex)), sizeof(tr_pex) );
117            tr_peerMgrAddPex( tor->handle->peerMgr, tor->info.hash, TR_PEER_FROM_CACHE, &pex );
118        }
119        tr_tordbg( tor, "Loaded %d peers from resume file", count );
120        ret = TR_FR_PEERS;
121    }
122
123    return ret;
124}
125
126static void
127savePriorities( tr_benc * dict, const tr_torrent * tor )
128{
129    const tr_info * inf = &tor->info;
130    const tr_file_index_t n = inf->fileCount;
131    tr_file_index_t i;
132    tr_benc * list;
133
134    list = tr_bencDictAddList( dict, KEY_PRIORITY, tor->info.fileCount );
135    for( i=0; i<n; ++i )
136        tr_bencInitInt( tr_bencListAdd( list ), inf->files[i].priority );
137}
138
139static uint64_t
140loadPriorities( tr_benc * dict, tr_torrent * tor )
141{
142    uint64_t ret = 0;
143    tr_info * inf = &tor->info;
144    const tr_file_index_t n = inf->fileCount;
145    tr_benc * list;
146
147    if( tr_bencDictFindList( dict, KEY_PRIORITY, &list ) && ( list->val.l.count == (int)n ) )
148    {
149        int64_t tmp;
150        tr_file_index_t i;
151        for( i=0; i<n; ++i )
152            if( tr_bencGetInt( &list->val.l.vals[i], &tmp ) )
153                inf->files[i].priority = tmp;
154        ret = TR_FR_PRIORITY;
155    }
156
157    return ret;
158}
159
160static void
161saveSpeedLimits( tr_benc * dict, const tr_torrent * tor )
162{
163    tr_benc * d = tr_bencDictAddDict( dict, KEY_SPEEDLIMIT, 4 );
164    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_DOWN_SPEED,
165                       tr_torrentGetSpeedLimit( tor, TR_DOWN ) );
166    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_DOWN_MODE,
167                       tr_torrentGetSpeedMode( tor, TR_DOWN ) );
168    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_UP_SPEED,
169                       tr_torrentGetSpeedLimit( tor, TR_UP ) );
170    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_UP_MODE,
171                       tr_torrentGetSpeedMode( tor, TR_UP ) );
172}
173
174static uint64_t
175loadSpeedLimits( tr_benc * dict, tr_torrent * tor )
176{
177    uint64_t ret = 0;
178    tr_benc * d;
179
180    if( tr_bencDictFindDict( dict, KEY_SPEEDLIMIT, &d ) )
181    {
182        int64_t i;
183        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_DOWN_SPEED, &i ) )
184            tr_torrentSetSpeedLimit( tor, TR_DOWN, i );
185        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_DOWN_MODE, &i ) )
186            tr_torrentSetSpeedMode( tor, TR_DOWN, i );
187        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_UP_SPEED, &i ) )
188            tr_torrentSetSpeedLimit( tor, TR_UP, i );
189        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_UP_MODE, &i ) )
190            tr_torrentSetSpeedMode( tor, TR_UP, i );
191        ret = TR_FR_SPEEDLIMIT;
192    }
193
194    return ret;
195}
196
197static void
198saveProgress( tr_benc * dict, const tr_torrent * tor )
199{
200    int i;
201    int n;
202    time_t * mtimes;
203    tr_benc * p;
204    tr_benc * m;
205    tr_benc * b;
206    const tr_bitfield * bitfield;
207
208    p = tr_bencDictAdd( dict, KEY_PROGRESS );
209    tr_bencInitDict( p, 2 );
210
211    /* add the mtimes */
212    m = tr_bencDictAdd( p, KEY_PROGRESS_MTIMES );
213    mtimes = getMTimes( tor, &n );
214    tr_bencInitList( m, n );
215    for( i=0; i<n; ++i ) {
216        if( !tr_torrentIsFileChecked( tor, i ) )
217            mtimes[i] = ~(time_t)0; /* force a recheck next time */
218        tr_bencInitInt( tr_bencListAdd( m ), mtimes[i] );
219    }
220
221    /* add the bitfield */
222    bitfield = tr_cpBlockBitfield( tor->completion );
223    b = tr_bencDictAdd( p, KEY_PROGRESS_BITFIELD );
224    tr_bencInitStrDupLen( b, (const char*)bitfield->bits, bitfield->len );
225
226    /* cleanup */
227    tr_free( mtimes );
228}
229
230static uint64_t
231loadProgress( tr_benc * dict, tr_torrent * tor )
232{
233    uint64_t ret = 0;
234    tr_benc * p;
235
236    if( tr_bencDictFindDict( dict, KEY_PROGRESS, &p ) )
237    {
238        tr_benc * m;
239        tr_benc * b;
240        int n;
241        time_t * curMTimes = getMTimes( tor, &n );
242
243        if( tr_bencDictFindList( p, KEY_PROGRESS_MTIMES, &m )
244            && ( m->val.l.count == (int64_t)tor->info.fileCount )
245            && ( m->val.l.count == n ) )
246        {
247            int i;
248            for( i=0; i<m->val.l.count; ++i ) 
249            {
250                int64_t tmp;
251                const time_t t = tr_bencGetInt( &m->val.l.vals[i], &tmp )
252                               ? tmp : ~(time_t)0;
253                if( curMTimes[i] == t )
254                    tr_torrentSetFileChecked( tor, i, TRUE );
255                else {
256                    tr_torrentSetFileChecked( tor, i, FALSE );
257                    tr_tordbg( tor, "File #%d needs to be verified", i );
258                }
259            }
260        }
261        else
262        {
263            tr_torrentUncheck( tor );
264            tr_tordbg( tor, "Torrent needs to be verified - unable to find mtimes" );
265        }
266
267        if(( b = tr_bencDictFindType( p, KEY_PROGRESS_BITFIELD, TYPE_STR )))
268        {
269            tr_bitfield tmp;
270            tmp.len = b->val.s.i;
271            tmp.bits = (uint8_t*) b->val.s.s;
272            if( tr_cpBlockBitfieldSet( tor->completion, &tmp ) ) {
273                tr_torrentUncheck( tor );
274                tr_tordbg( tor, "Torrent needs to be verified - error loading bitfield" );
275            }
276        }
277        else
278        {
279            tr_torrentUncheck( tor );
280            tr_tordbg( tor, "Torrent needs to be verified - unable to find bitfield" );
281        }
282       
283        tr_free( curMTimes );
284        ret = TR_FR_PROGRESS;
285    }
286
287    return ret;
288}
289
290void
291tr_torrentSaveResume( const tr_torrent * tor )
292{
293    tr_benc top;
294    char * encoded;
295    int len;
296
297    /* populate the bencoded data */
298    tr_bencInitDict( &top, 10 );
299    tr_bencDictAddInt( &top, KEY_CORRUPT, tor->corruptPrev + tor->corruptCur );
300    tr_bencDictAddStr( &top, KEY_DESTINATION, tor->destination );
301    tr_bencDictAddInt( &top, KEY_DOWNLOADED, tor->downloadedPrev + tor->downloadedCur );
302    tr_bencDictAddInt( &top, KEY_UPLOADED, tor->uploadedPrev + tor->uploadedCur );
303    tr_bencDictAddInt( &top, KEY_MAX_PEERS, tor->maxConnectedPeers );
304    tr_bencDictAddInt( &top, KEY_PAUSED, tor->isRunning?0:1 );
305    savePeers( &top, tor );
306    savePriorities( &top, tor );
307    saveProgress( &top, tor );
308    saveSpeedLimits( &top, tor );
309
310    /* save the bencoded data */
311    if(( encoded = tr_bencSave( &top, &len )))
312    {
313        char filename[MAX_PATH_LENGTH];
314        FILE * fp;
315        getResumeFilename( filename, sizeof( filename ), tor );
316        fp = fopen( filename, "wb+" );
317        fwrite( encoded, len, 1, fp );
318        fclose( fp );
319        tr_free( encoded );
320    }
321
322    /* cleanup */
323    tr_bencFree( &top );
324}
325
326uint64_t
327tr_torrentLoadResume( tr_torrent    * tor,
328                      uint64_t        fieldsToLoad,
329                      const tr_ctor * ctor )
330{
331    int64_t i;
332    const char * str;
333    int benc_loaded = FALSE;
334    uint64_t fieldsLoaded = 0;
335    uint8_t * content = NULL;
336    size_t contentLen;
337    char filename[MAX_PATH_LENGTH];
338    tr_benc top;
339
340    getResumeFilename( filename, sizeof( filename ), tor );
341
342    content = tr_loadFile( filename, &contentLen );
343    benc_loaded = content && !tr_bencLoad( content, contentLen, &top, NULL );
344    if( !benc_loaded ) {
345        tr_free( content );
346        tr_tordbg( tor, "Couldn't read \"%s\"; trying old resume file format.", filename );
347        return tr_fastResumeLoad( tor, fieldsToLoad, ctor );
348    }
349
350    tr_tordbg( tor, "Read resume file \"%s\"", filename );
351
352    if( tr_bencDictFindInt( &top, KEY_CORRUPT, &i ) ) {
353        tor->corruptPrev = i;
354        fieldsLoaded |= TR_FR_CORRUPT;
355    }
356
357    if( tr_bencDictFindStr( &top, KEY_DESTINATION, &str ) ) {
358        tr_free( tor->destination );
359        tor->destination = tr_strdup( str );
360        fieldsLoaded |= TR_FR_DESTINATION;
361    }
362
363    if( tr_bencDictFindInt( &top, KEY_DOWNLOADED, &i ) ) {
364        tor->downloadedPrev = i;
365        fieldsLoaded |= TR_FR_DOWNLOADED;
366    }
367
368    if( tr_bencDictFindInt( &top, KEY_UPLOADED, &i ) ) {
369        tor->uploadedPrev = i;
370        fieldsLoaded |= TR_FR_UPLOADED;
371    }
372
373    if( tr_bencDictFindInt( &top, KEY_MAX_PEERS, &i ) ) {
374        tor->maxConnectedPeers = i;
375        fieldsLoaded |= TR_FR_MAX_PEERS;
376    }
377
378    if( tr_bencDictFindInt( &top, KEY_PAUSED, &i ) ) {
379        tor->isRunning = i ? 0 : 1;
380        fieldsLoaded |= TR_FR_RUN;
381    }
382
383    fieldsLoaded |= loadPeers( &top, tor );
384    fieldsLoaded |= loadPriorities( &top, tor );
385    fieldsLoaded |= loadProgress( &top, tor );
386    fieldsLoaded |= loadSpeedLimits( &top, tor );
387
388    tr_bencFree( &top );
389    return fieldsLoaded;
390}
391
392void
393tr_torrentRemoveResume( const tr_torrent * tor )
394{
395    char filename[MAX_PATH_LENGTH];
396    getResumeFilename( filename, sizeof( filename ), tor );
397    unlink( filename );
398    tr_fastResumeRemove( tor );
399}
Note: See TracBrowser for help on using the repository browser.