source: trunk/libtransmission/resume.c @ 5621

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

add more debugging messages for loading progress from the bencoded resume file

File size: 13.2 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 <unistd.h> /* unlink */
14
15#include <string.h>
16
17#include "transmission.h"
18#include "bencode.h"
19#include "completion.h"
20#include "fastresume.h"
21#include "peer-mgr.h" /* pex */
22#include "platform.h" /* tr_getResumeDir */
23#include "resume.h"
24#include "torrent.h"
25#include "utils.h" /* tr_buildPath */
26
27#define KEY_CORRUPT     "corrupt"
28#define KEY_DESTINATION "destination"
29#define KEY_DND         "dnd"
30#define KEY_DOWNLOADED  "downloaded"
31#define KEY_MAX_PEERS   "max-peers"
32#define KEY_PAUSED      "paused"
33#define KEY_PEERS       "peers"
34#define KEY_PRIORITY    "priority"
35#define KEY_PROGRESS    "progress"
36#define KEY_SPEEDLIMIT  "speed-limit"
37#define KEY_UPLOADED    "uploaded"
38
39#define KEY_SPEEDLIMIT_DOWN_SPEED "down-speed"
40#define KEY_SPEEDLIMIT_DOWN_MODE  "down-mode"
41#define KEY_SPEEDLIMIT_UP_SPEED   "up-speed"
42#define KEY_SPEEDLIMIT_UP_MODE    "up-mode"
43
44#define KEY_PROGRESS_MTIMES   "mtimes"
45#define KEY_PROGRESS_BITFIELD "bitfield"
46
47static void
48getResumeFilename( char * buf, size_t buflen, const tr_torrent * tor )
49{
50    const char * dir = tr_getResumeDir( tor->handle );
51    char base[4096];
52    snprintf( base, sizeof( base ), "%s.%16.16s.resume",
53              tor->info.name,
54              tor->info.hashString );
55    tr_buildPath( buf, buflen, dir, base, NULL );
56}
57
58/***
59****
60***/
61
62static void
63savePeers( tr_benc * dict, const tr_torrent * tor )
64{
65    tr_pex * pex = NULL;
66    const int count = tr_peerMgrGetPeers( tor->handle->peerMgr,
67                                          tor->info.hash, &pex );
68    if( count > 0 )
69        tr_bencInitStrDupLen( tr_bencDictAdd( dict, KEY_PEERS ),
70                              (const char*)pex, sizeof(tr_pex)*count );
71    tr_free( pex );
72}
73
74static uint64_t
75loadPeers( tr_benc * dict, tr_torrent * tor )
76{
77    uint64_t ret = 0;
78    tr_benc * p;
79
80    if(( p = tr_bencDictFindType( dict, KEY_PEERS, TYPE_STR )))
81    {
82        int i;
83        const char * str = p->val.s.s;
84        const size_t len = p->val.s.i;
85        const int count = len / sizeof( tr_pex );
86        for( i=0; i<count; ++i ) {
87            tr_pex pex;
88            memcpy( &pex, str + (i*sizeof(tr_pex)), sizeof(tr_pex) );
89            tr_peerMgrAddPex( tor->handle->peerMgr,
90                              tor->info.hash, TR_PEER_FROM_CACHE, &pex );
91        }
92        tr_tordbg( tor, "Loaded %d peers from resume file", count );
93        ret = TR_FR_PEERS;
94    }
95
96    return ret;
97}
98
99/***
100****
101***/
102
103static void
104saveDND( tr_benc * dict, const tr_torrent * tor )
105{
106    const tr_info * inf = &tor->info;
107    const tr_file_index_t n = inf->fileCount;
108    tr_file_index_t i;
109    tr_benc * list;
110
111    list = tr_bencDictAddList( dict, KEY_DND, n );
112    for( i=0; i<n; ++i )
113        tr_bencInitInt( tr_bencListAdd( list ), inf->files[i].dnd ? 1 : 0 );
114}
115
116static uint64_t
117loadDND( tr_benc * dict, tr_torrent * tor )
118{
119    uint64_t ret = 0;
120    tr_info * inf = &tor->info;
121    const tr_file_index_t n = inf->fileCount;
122    tr_benc * list = NULL;
123
124    if( tr_bencDictFindList( dict, KEY_DND, &list )
125        && ( list->val.l.count == (int)n ) )
126    {
127        int64_t tmp;
128        tr_file_index_t * dl = tr_new( tr_file_index_t, n );
129        tr_file_index_t * dnd = tr_new( tr_file_index_t, n );
130        tr_file_index_t i, dlCount=0, dndCount=0;
131
132        for( i=0; i<n; ++i ) {
133            if( tr_bencGetInt( &list->val.l.vals[i], &tmp ) && tmp )
134                dnd[dndCount++] = i;
135            else
136                dl[dlCount++] = i;
137        }
138
139        if( dndCount ) {
140            tr_torrentInitFileDLs ( tor, dnd, dndCount, FALSE );
141            tr_tordbg( tor, "Resume file found %d files listed as dnd", dndCount );
142        }
143        if( dlCount ) {
144            tr_torrentInitFileDLs ( tor, dl, dlCount, TRUE );
145            tr_tordbg( tor, "Resume file found %d files marked for download", dlCount );
146        }
147
148        tr_free( dnd );
149        tr_free( dl );
150        ret = TR_FR_PRIORITY;
151    }
152    else
153    {
154        tr_tordbg( tor, "Couldn't load DND flags.  dnd list (%p) has %d children; torrent has %d files",
155                   ( list ? list->val.l.count : -1 ), (int)n );
156    }
157
158    return ret;
159}
160
161/***
162****
163***/
164
165static void
166savePriorities( tr_benc * dict, const tr_torrent * tor )
167{
168    const tr_info * inf = &tor->info;
169    const tr_file_index_t n = inf->fileCount;
170    tr_file_index_t i;
171    tr_benc * list;
172
173    list = tr_bencDictAddList( dict, KEY_PRIORITY, n );
174    for( i=0; i<n; ++i )
175        tr_bencInitInt( tr_bencListAdd( list ), inf->files[i].priority );
176}
177
178static uint64_t
179loadPriorities( tr_benc * dict, tr_torrent * tor )
180{
181    uint64_t ret = 0;
182    tr_info * inf = &tor->info;
183    const tr_file_index_t n = inf->fileCount;
184    tr_benc * list;
185
186    if( tr_bencDictFindList( dict, KEY_PRIORITY, &list )
187        && ( list->val.l.count == (int)n ) )
188    {
189        int64_t tmp;
190        tr_file_index_t i;
191        for( i=0; i<n; ++i )
192            if( tr_bencGetInt( &list->val.l.vals[i], &tmp ) )
193                inf->files[i].priority = tmp;
194        ret = TR_FR_PRIORITY;
195    }
196
197    return ret;
198}
199
200/***
201****
202***/
203
204static void
205saveSpeedLimits( tr_benc * dict, const tr_torrent * tor )
206{
207    tr_benc * d = tr_bencDictAddDict( dict, KEY_SPEEDLIMIT, 4 );
208    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_DOWN_SPEED,
209                       tr_torrentGetSpeedLimit( tor, TR_DOWN ) );
210    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_DOWN_MODE,
211                       tr_torrentGetSpeedMode( tor, TR_DOWN ) );
212    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_UP_SPEED,
213                       tr_torrentGetSpeedLimit( tor, TR_UP ) );
214    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_UP_MODE,
215                       tr_torrentGetSpeedMode( tor, TR_UP ) );
216}
217
218static uint64_t
219loadSpeedLimits( tr_benc * dict, tr_torrent * tor )
220{
221    uint64_t ret = 0;
222    tr_benc * d;
223
224    if( tr_bencDictFindDict( dict, KEY_SPEEDLIMIT, &d ) )
225    {
226        int64_t i;
227        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_DOWN_SPEED, &i ) )
228            tr_torrentSetSpeedLimit( tor, TR_DOWN, i );
229        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_DOWN_MODE, &i ) )
230            tr_torrentSetSpeedMode( tor, TR_DOWN, i );
231        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_UP_SPEED, &i ) )
232            tr_torrentSetSpeedLimit( tor, TR_UP, i );
233        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_UP_MODE, &i ) )
234            tr_torrentSetSpeedMode( tor, TR_UP, i );
235        ret = TR_FR_SPEEDLIMIT;
236    }
237
238    return ret;
239}
240
241/***
242****
243***/
244
245static void
246saveProgress( tr_benc * dict, const tr_torrent * tor )
247{
248    int i;
249    int n;
250    time_t * mtimes;
251    tr_benc * p;
252    tr_benc * m;
253    tr_benc * b;
254    const tr_bitfield * bitfield;
255
256    p = tr_bencDictAdd( dict, KEY_PROGRESS );
257    tr_bencInitDict( p, 2 );
258
259    /* add the mtimes */
260    mtimes = tr_torrentGetMTimes( tor, &n );
261    m = tr_bencDictAddList( p, KEY_PROGRESS_MTIMES, n );
262    for( i=0; i<n; ++i ) {
263        if( tr_torrentIsFileChecked( tor, i ) )
264            mtimes[i] = ~(time_t)0; /* force a recheck */
265        tr_bencListAddInt( m, mtimes[i] );
266    }
267
268    /* add the bitfield */
269    bitfield = tr_cpBlockBitfield( tor->completion );
270    b = tr_bencDictAdd( p, KEY_PROGRESS_BITFIELD );
271    tr_bencInitStrDupLen( b, (const char*)bitfield->bits, bitfield->len );
272
273    /* cleanup */
274    tr_free( mtimes );
275}
276
277static uint64_t
278loadProgress( tr_benc * dict, tr_torrent * tor )
279{
280    uint64_t ret = 0;
281    tr_benc * p;
282
283    if( tr_bencDictFindDict( dict, KEY_PROGRESS, &p ) )
284    {
285        tr_benc * m;
286        tr_benc * b;
287        int n;
288        time_t * curMTimes = tr_torrentGetMTimes( tor, &n );
289
290        if( tr_bencDictFindList( p, KEY_PROGRESS_MTIMES, &m )
291            && ( m->val.l.count == (int64_t)tor->info.fileCount )
292            && ( m->val.l.count == n ) )
293        {
294            int i;
295            for( i=0; i<n; ++i )
296            {
297                int64_t tmp;
298                if( !tr_bencGetInt( &m->val.l.vals[i], &tmp ) ) {
299                    tr_tordbg( tor, "File #%d needs to be verified - couldn't find benc entry", i );
300                    tr_torrentSetFileChecked( tor, i, FALSE );
301                } else {
302                    const time_t t = (time_t) tmp;
303                    if( t == curMTimes[i] )
304                        tr_torrentSetFileChecked( tor, i, TRUE );
305                    else {
306                        tr_tordbg( tor, "File #%d needs to be verified - times %lu and %lu don't match", t, curMTimes[i] );
307                        tr_torrentSetFileChecked( tor, i, FALSE );
308                    }
309                }
310            }
311        }
312        else
313        {
314            tr_torrentUncheck( tor );
315            tr_tordbg( tor, "Torrent needs to be verified - unable to find mtimes" );
316        }
317
318        if(( b = tr_bencDictFindType( p, KEY_PROGRESS_BITFIELD, TYPE_STR )))
319        {
320            tr_bitfield tmp;
321            tmp.len = b->val.s.i;
322            tmp.bits = (uint8_t*) b->val.s.s;
323            if( tr_cpBlockBitfieldSet( tor->completion, &tmp ) ) {
324                tr_torrentUncheck( tor );
325                tr_tordbg( tor, "Torrent needs to be verified - error loading bitfield" );
326            }
327        }
328        else
329        {
330            tr_torrentUncheck( tor );
331            tr_tordbg( tor, "Torrent needs to be verified - unable to find bitfield" );
332        }
333       
334        tr_free( curMTimes );
335        ret = TR_FR_PROGRESS;
336    }
337
338    return ret;
339}
340
341/***
342****
343***/
344
345void
346tr_torrentSaveResume( const tr_torrent * tor )
347{
348    tr_benc top;
349    char filename[MAX_PATH_LENGTH];
350
351    tr_bencInitDict( &top, 10 );
352    tr_bencDictAddInt( &top, KEY_CORRUPT,
353                             tor->corruptPrev + tor->corruptCur );
354    tr_bencDictAddStr( &top, KEY_DESTINATION,
355                             tor->destination );
356    tr_bencDictAddInt( &top, KEY_DOWNLOADED,
357                             tor->downloadedPrev + tor->downloadedCur );
358    tr_bencDictAddInt( &top, KEY_UPLOADED,
359                             tor->uploadedPrev + tor->uploadedCur );
360    tr_bencDictAddInt( &top, KEY_MAX_PEERS,
361                             tor->maxConnectedPeers );
362    tr_bencDictAddInt( &top, KEY_PAUSED,
363                             tor->isRunning ? 0 : 1 );
364    savePeers( &top, tor );
365    savePriorities( &top, tor );
366    saveDND( &top, tor );
367    saveProgress( &top, tor );
368    saveSpeedLimits( &top, tor );
369
370    getResumeFilename( filename, sizeof( filename ), tor );
371    tr_bencSaveFile( filename, &top );
372
373    tr_bencFree( &top );
374}
375
376uint64_t
377tr_torrentLoadResume( tr_torrent    * tor,
378                      uint64_t        fieldsToLoad,
379                      const tr_ctor * ctor )
380{
381    int64_t i;
382    const char * str;
383    uint64_t fieldsLoaded = 0;
384    char filename[MAX_PATH_LENGTH];
385    tr_benc top;
386
387    getResumeFilename( filename, sizeof( filename ), tor );
388
389    if( tr_bencLoadFile( filename, &top ) )
390    {
391        tr_tordbg( tor, "Couldn't read \"%s\"; trying old format.", filename );
392        fieldsLoaded = tr_fastResumeLoad( tor, fieldsToLoad, ctor );
393
394        if( ( fieldsLoaded != 0 ) && ( fieldsToLoad == ~(uint64_t)0 ) )
395        {
396            tr_torrentSaveResume( tor );
397            tr_fastResumeRemove( tor );
398            tr_tordbg( tor, "Migrated resume file to \"%s\"", filename );
399        }
400
401        return fieldsLoaded;
402    }
403
404    tr_tordbg( tor, "Read resume file \"%s\"", filename );
405
406    if( ( fieldsToLoad & TR_FR_CORRUPT )
407            && tr_bencDictFindInt( &top, KEY_CORRUPT, &i ) ) {
408        tor->corruptPrev = i;
409        fieldsLoaded |= TR_FR_CORRUPT;
410    }
411
412    if( ( fieldsToLoad & TR_FR_DESTINATION )
413            && tr_bencDictFindStr( &top, KEY_DESTINATION, &str ) ) {
414        tr_free( tor->destination );
415        tor->destination = tr_strdup( str );
416        fieldsLoaded |= TR_FR_DESTINATION;
417    }
418
419    if( ( fieldsToLoad & TR_FR_DOWNLOADED )
420            && tr_bencDictFindInt( &top, KEY_DOWNLOADED, &i ) ) {
421        tor->downloadedPrev = i;
422        fieldsLoaded |= TR_FR_DOWNLOADED;
423    }
424
425    if( ( fieldsToLoad & TR_FR_UPLOADED )
426            && tr_bencDictFindInt( &top, KEY_UPLOADED, &i ) ) {
427        tor->uploadedPrev = i;
428        fieldsLoaded |= TR_FR_UPLOADED;
429    }
430
431    if( ( fieldsToLoad & TR_FR_MAX_PEERS )
432            && tr_bencDictFindInt( &top, KEY_MAX_PEERS, &i ) ) {
433        tor->maxConnectedPeers = i;
434        fieldsLoaded |= TR_FR_MAX_PEERS;
435    }
436
437    if( ( fieldsToLoad & TR_FR_RUN )
438            && tr_bencDictFindInt( &top, KEY_PAUSED, &i ) ) {
439        tor->isRunning = i ? 0 : 1;
440        fieldsLoaded |= TR_FR_RUN;
441    }
442
443    if( fieldsToLoad & TR_FR_PEERS )
444        fieldsLoaded |= loadPeers( &top, tor );
445
446    if( fieldsToLoad & TR_FR_PRIORITY )
447        fieldsLoaded |= loadPriorities( &top, tor );
448
449    if( fieldsToLoad & TR_FR_PROGRESS )
450        fieldsLoaded |= loadProgress( &top, tor );
451
452    if( fieldsToLoad & TR_FR_DND )
453        fieldsLoaded |= loadDND( &top, tor );
454
455    if( fieldsToLoad & TR_FR_SPEEDLIMIT )
456        fieldsLoaded |= loadSpeedLimits( &top, tor );
457
458    tr_bencFree( &top );
459    return fieldsLoaded;
460}
461
462void
463tr_torrentRemoveResume( const tr_torrent * tor )
464{
465    char filename[MAX_PATH_LENGTH];
466    getResumeFilename( filename, sizeof( filename ), tor );
467    unlink( filename );
468    tr_fastResumeRemove( tor );
469}
Note: See TracBrowser for help on using the repository browser.