source: trunk/libtransmission/resume.c @ 7397

Last change on this file since 7397 was 7397, checked in by charles, 12 years ago

(trunk libT) add ipv6 support by jhujhiti. I think this is the largest user-contributed patch we've ever used... thanks jhujhiti :)

  • Property svn:keywords set to Date Rev Author Id
File size: 17.4 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: resume.c 7397 2008-12-15 00:17:08Z charles $
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_ACTIVITY_DATE   "activity-date"
28#define KEY_ADDED_DATE      "added-date"
29#define KEY_CORRUPT         "corrupt"
30#define KEY_DONE_DATE       "done-date"
31#define KEY_DOWNLOAD_DIR    "destination"
32#define KEY_DND             "dnd"
33#define KEY_DOWNLOADED      "downloaded"
34#define KEY_MAX_PEERS       "max-peers"
35#define KEY_PAUSED          "paused"
36#define KEY_PEERS           "peers"
37#define KEY_PEERS6          "peers6"
38#define KEY_PRIORITY        "priority"
39#define KEY_PROGRESS        "progress"
40#define KEY_SPEEDLIMIT      "speed-limit"
41#define KEY_UPLOADED        "uploaded"
42
43#define KEY_SPEEDLIMIT_DOWN_SPEED "down-speed"
44#define KEY_SPEEDLIMIT_DOWN_MODE  "down-mode"
45#define KEY_SPEEDLIMIT_UP_SPEED   "up-speed"
46#define KEY_SPEEDLIMIT_UP_MODE    "up-mode"
47
48#define KEY_PROGRESS_MTIMES   "mtimes"
49#define KEY_PROGRESS_BITFIELD "bitfield"
50
51static char*
52getResumeFilename( const tr_torrent * tor )
53{
54    return tr_strdup_printf( "%s%c%s.%16.16s.resume",
55                             tr_getResumeDir( tor->session ),
56                             TR_PATH_DELIMITER,
57                             tor->info.name,
58                             tor->info.hashString );
59}
60
61/***
62****
63***/
64
65static void
66savePeers( tr_benc *          dict,
67           const tr_torrent * tor )
68{
69    tr_pex * pex = NULL;
70    int count = tr_peerMgrGetPeers( tor->session->peerMgr,
71                                    tor->info.hash, &pex, TR_AF_INET );
72
73    if( count > 0 )
74        tr_bencDictAddRaw( dict, KEY_PEERS, pex, sizeof( tr_pex ) * count );
75   
76    count = tr_peerMgrGetPeers( tor->session->peerMgr, tor->info.hash, &pex,
77                                TR_AF_INET6 );
78    if( count > 0 )
79        tr_bencDictAddRaw( dict, KEY_PEERS6, pex, sizeof( tr_pex ) * count );
80   
81    tr_free( pex );
82}
83
84static uint64_t
85loadPeers( tr_benc *    dict,
86           tr_torrent * tor )
87{
88    uint64_t        ret = 0;
89    const uint8_t * str;
90    size_t          len;
91
92    if( tr_bencDictFindRaw( dict, KEY_PEERS, &str, &len ) )
93    {
94        int       i;
95        const int count = len / sizeof( tr_pex );
96        for( i = 0; i < count; ++i )
97        {
98            tr_pex pex;
99            memcpy( &pex, str + ( i * sizeof( tr_pex ) ), sizeof( tr_pex ) );
100            tr_peerMgrAddPex( tor->session->peerMgr,
101                              tor->info.hash, TR_PEER_FROM_CACHE, &pex );
102        }
103        tr_tordbg( tor, "Loaded %d IPv4 peers from resume file", count );
104        ret = TR_FR_PEERS;
105    }
106   
107    if( tr_bencDictFindRaw( dict, KEY_PEERS6, &str, &len ) )
108    {
109        int       i;
110        const int count = len / sizeof( tr_pex );
111        for( i = 0; i < count; ++i )
112        {
113            tr_pex pex;
114            memcpy( &pex, str + ( i * sizeof( tr_pex ) ), sizeof( tr_pex ) );
115            tr_peerMgrAddPex( tor->session->peerMgr,
116                              tor->info.hash, TR_PEER_FROM_CACHE, &pex );
117        }
118        tr_tordbg( tor, "Loaded %d IPv6 peers from resume file", count );
119        ret = TR_FR_PEERS;
120    }
121
122    return ret;
123}
124
125/***
126****
127***/
128
129static void
130saveDND( tr_benc *          dict,
131         const tr_torrent * tor )
132{
133    const tr_info *       inf = tr_torrentInfo( tor );
134    const tr_file_index_t n = inf->fileCount;
135    tr_file_index_t       i;
136    tr_benc *             list;
137
138    list = tr_bencDictAddList( dict, KEY_DND, n );
139    for( i = 0; i < n; ++i )
140        tr_bencListAddInt( list, inf->files[i].dnd ? 1 : 0 );
141}
142
143static uint64_t
144loadDND( tr_benc *    dict,
145         tr_torrent * tor )
146{
147    uint64_t              ret = 0;
148    tr_info *             inf = &tor->info;
149    const tr_file_index_t n = inf->fileCount;
150    tr_benc *             list = NULL;
151
152    if( tr_bencDictFindList( dict, KEY_DND, &list )
153      && ( tr_bencListSize( list ) == n ) )
154    {
155        int64_t           tmp;
156        tr_file_index_t * dl = tr_new( tr_file_index_t, n );
157        tr_file_index_t * dnd = tr_new( tr_file_index_t, n );
158        tr_file_index_t   i, dlCount = 0, dndCount = 0;
159
160        for( i = 0; i < n; ++i )
161        {
162            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) && tmp )
163                dnd[dndCount++] = i;
164            else
165                dl[dlCount++] = i;
166        }
167
168        if( dndCount )
169        {
170            tr_torrentInitFileDLs ( tor, dnd, dndCount, FALSE );
171            tr_tordbg( tor, "Resume file found %d files listed as dnd",
172                       dndCount );
173        }
174        if( dlCount )
175        {
176            tr_torrentInitFileDLs ( tor, dl, dlCount, TRUE );
177            tr_tordbg( tor,
178                       "Resume file found %d files marked for download",
179                       dlCount );
180        }
181
182        tr_free( dnd );
183        tr_free( dl );
184        ret = TR_FR_PRIORITY;
185    }
186    else
187    {
188        tr_tordbg(
189            tor,
190            "Couldn't load DND flags.  dnd list (%p) has %zu children; torrent has %d files",
191            list, tr_bencListSize( list ), (int)n );
192    }
193
194    return ret;
195}
196
197/***
198****
199***/
200
201static void
202savePriorities( tr_benc *          dict,
203                const tr_torrent * tor )
204{
205    const tr_info *       inf = tr_torrentInfo( tor );
206    const tr_file_index_t n = inf->fileCount;
207    tr_file_index_t       i;
208    tr_benc *             list;
209
210    list = tr_bencDictAddList( dict, KEY_PRIORITY, n );
211    for( i = 0; i < n; ++i )
212        tr_bencListAddInt( list, inf->files[i].priority );
213}
214
215static uint64_t
216loadPriorities( tr_benc *    dict,
217                tr_torrent * tor )
218{
219    uint64_t              ret = 0;
220    tr_info *             inf = &tor->info;
221    const tr_file_index_t n = inf->fileCount;
222    tr_benc *             list;
223
224    if( tr_bencDictFindList( dict, KEY_PRIORITY, &list )
225      && ( tr_bencListSize( list ) == n ) )
226    {
227        int64_t         tmp;
228        tr_file_index_t i;
229        for( i = 0; i < n; ++i )
230            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) )
231                inf->files[i].priority = tmp;
232        ret = TR_FR_PRIORITY;
233    }
234
235    return ret;
236}
237
238/***
239****
240***/
241
242static void
243saveSpeedLimits( tr_benc *          dict,
244                 const tr_torrent * tor )
245{
246    tr_benc * d = tr_bencDictAddDict( dict, KEY_SPEEDLIMIT, 4 );
247
248    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_DOWN_SPEED,
249                      tr_torrentGetSpeedLimit( tor, TR_DOWN ) );
250    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_DOWN_MODE,
251                      tr_torrentGetSpeedMode( tor, TR_DOWN ) );
252    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_UP_SPEED,
253                      tr_torrentGetSpeedLimit( tor, TR_UP ) );
254    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_UP_MODE,
255                      tr_torrentGetSpeedMode( tor, TR_UP ) );
256}
257
258static uint64_t
259loadSpeedLimits( tr_benc *    dict,
260                 tr_torrent * tor )
261{
262    uint64_t  ret = 0;
263    tr_benc * d;
264
265    if( tr_bencDictFindDict( dict, KEY_SPEEDLIMIT, &d ) )
266    {
267        int64_t i;
268        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_DOWN_SPEED, &i ) )
269            tr_torrentSetSpeedLimit( tor, TR_DOWN, i );
270        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_DOWN_MODE, &i ) )
271            tr_torrentSetSpeedMode( tor, TR_DOWN, i );
272        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_UP_SPEED, &i ) )
273            tr_torrentSetSpeedLimit( tor, TR_UP, i );
274        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_UP_MODE, &i ) )
275            tr_torrentSetSpeedMode( tor, TR_UP, i );
276        ret = TR_FR_SPEEDLIMIT;
277    }
278
279    return ret;
280}
281
282/***
283****
284***/
285
286static void
287saveProgress( tr_benc *          dict,
288              const tr_torrent * tor )
289{
290    size_t              i, n;
291    time_t *            mtimes;
292    tr_benc *           p;
293    tr_benc *           m;
294    const tr_bitfield * bitfield;
295
296    p = tr_bencDictAdd( dict, KEY_PROGRESS );
297    tr_bencInitDict( p, 2 );
298
299    /* add the mtimes */
300    mtimes = tr_torrentGetMTimes( tor, &n );
301    m = tr_bencDictAddList( p, KEY_PROGRESS_MTIMES, n );
302    for( i = 0; i < n; ++i )
303    {
304        if( !tr_torrentIsFileChecked( tor, i ) )
305            mtimes[i] = ~(time_t)0; /* force a recheck */
306        tr_bencListAddInt( m, mtimes[i] );
307    }
308
309    /* add the bitfield */
310    bitfield = tr_cpBlockBitfield( tor->completion );
311    tr_bencDictAddRaw( p, KEY_PROGRESS_BITFIELD,
312                       bitfield->bits, bitfield->byteCount );
313
314    /* cleanup */
315    tr_free( mtimes );
316}
317
318static uint64_t
319loadProgress( tr_benc *    dict,
320              tr_torrent * tor )
321{
322    uint64_t  ret = 0;
323    tr_benc * p;
324
325    if( tr_bencDictFindDict( dict, KEY_PROGRESS, &p ) )
326    {
327        const uint8_t * raw;
328        size_t          rawlen;
329        tr_benc *       m;
330        size_t          n;
331        time_t *        curMTimes = tr_torrentGetMTimes( tor, &n );
332
333        if( tr_bencDictFindList( p, KEY_PROGRESS_MTIMES, &m )
334          && ( n == tor->info.fileCount )
335          && ( n == tr_bencListSize( m ) ) )
336        {
337            size_t i;
338            for( i = 0; i < n; ++i )
339            {
340                int64_t tmp;
341                if( !tr_bencGetInt( tr_bencListChild( m, i ), &tmp ) )
342                {
343                    tr_tordbg(
344                        tor,
345                        "File #%zu needs to be verified - couldn't find benc entry",
346                        i );
347                    tr_torrentSetFileChecked( tor, i, FALSE );
348                }
349                else
350                {
351                    const time_t t = (time_t) tmp;
352                    if( t == curMTimes[i] )
353                        tr_torrentSetFileChecked( tor, i, TRUE );
354                    else
355                    {
356                        tr_tordbg(
357                            tor,
358                            "File #%zu needs to be verified - times %lu and %lu don't match",
359                            i, t, curMTimes[i] );
360                        tr_torrentSetFileChecked( tor, i, FALSE );
361                    }
362                }
363            }
364        }
365        else
366        {
367            tr_torrentUncheck( tor );
368            tr_tordbg(
369                tor, "Torrent needs to be verified - unable to find mtimes" );
370        }
371
372        if( tr_bencDictFindRaw( p, KEY_PROGRESS_BITFIELD, &raw, &rawlen ) )
373        {
374            tr_bitfield tmp;
375            tmp.byteCount = rawlen;
376            tmp.bitCount = tmp.byteCount * 8;
377            tmp.bits = (uint8_t*) raw;
378            if( !tr_cpBlockBitfieldSet( tor->completion, &tmp ) )
379            {
380                tr_torrentUncheck( tor );
381                tr_tordbg(
382                    tor,
383                    "Torrent needs to be verified - error loading bitfield" );
384            }
385        }
386        else
387        {
388            tr_torrentUncheck( tor );
389            tr_tordbg(
390                tor,
391                "Torrent needs to be verified - unable to find bitfield" );
392        }
393
394        tr_free( curMTimes );
395        ret = TR_FR_PROGRESS;
396    }
397
398    return ret;
399}
400
401/***
402****
403***/
404
405void
406tr_torrentSaveResume( const tr_torrent * tor )
407{
408    tr_benc top;
409    char *  filename;
410
411    if( !tor )
412        return;
413
414    tr_bencInitDict( &top, 14 );
415    tr_bencDictAddInt( &top, KEY_ACTIVITY_DATE,
416                       tor->activityDate );
417    tr_bencDictAddInt( &top, KEY_ADDED_DATE,
418                       tor->addedDate );
419    tr_bencDictAddInt( &top, KEY_CORRUPT,
420                       tor->corruptPrev + tor->corruptCur );
421    tr_bencDictAddInt( &top, KEY_DONE_DATE,
422                       tor->doneDate );
423    tr_bencDictAddStr( &top, KEY_DOWNLOAD_DIR,
424                       tor->downloadDir );
425    tr_bencDictAddInt( &top, KEY_DOWNLOADED,
426                       tor->downloadedPrev + tor->downloadedCur );
427    tr_bencDictAddInt( &top, KEY_UPLOADED,
428                       tor->uploadedPrev + tor->uploadedCur );
429    tr_bencDictAddInt( &top, KEY_MAX_PEERS,
430                       tor->maxConnectedPeers );
431    tr_bencDictAddInt( &top, KEY_PAUSED,
432                       tor->isRunning ? 0 : 1 );
433    savePeers( &top, tor );
434    savePriorities( &top, tor );
435    saveDND( &top, tor );
436    saveProgress( &top, tor );
437    saveSpeedLimits( &top, tor );
438
439    filename = getResumeFilename( tor );
440    tr_bencSaveFile( filename, &top );
441    tr_free( filename );
442
443    tr_bencFree( &top );
444}
445
446static uint64_t
447loadFromFile( tr_torrent * tor,
448              uint64_t     fieldsToLoad )
449{
450    int64_t      i;
451    const char * str;
452    uint64_t     fieldsLoaded = 0;
453    char *       filename;
454    tr_benc      top;
455
456    filename = getResumeFilename( tor );
457
458    if( tr_bencLoadFile( filename, &top ) )
459    {
460        tr_tordbg( tor, "Couldn't read \"%s\"; trying old format.",
461                   filename );
462        fieldsLoaded = tr_fastResumeLoad( tor, fieldsToLoad );
463
464        if( ( fieldsLoaded != 0 ) && ( fieldsToLoad == ~(uint64_t)0 ) )
465        {
466            tr_torrentSaveResume( tor );
467            tr_fastResumeRemove( tor );
468            tr_tordbg( tor, "Migrated resume file to \"%s\"", filename );
469        }
470
471        tr_free( filename );
472        return fieldsLoaded;
473    }
474
475    tr_tordbg( tor, "Read resume file \"%s\"", filename );
476
477    if( ( fieldsToLoad & TR_FR_CORRUPT )
478      && tr_bencDictFindInt( &top, KEY_CORRUPT, &i ) )
479    {
480        tor->corruptPrev = i;
481        fieldsLoaded |= TR_FR_CORRUPT;
482    }
483
484    if( ( fieldsToLoad & ( TR_FR_PROGRESS | TR_FR_DOWNLOAD_DIR ) )
485      && ( tr_bencDictFindStr( &top, KEY_DOWNLOAD_DIR, &str ) )
486      && ( str && *str ) )
487    {
488        tr_free( tor->downloadDir );
489        tor->downloadDir = tr_strdup( str );
490        fieldsLoaded |= TR_FR_DOWNLOAD_DIR;
491    }
492
493    if( ( fieldsToLoad & TR_FR_DOWNLOADED )
494      && tr_bencDictFindInt( &top, KEY_DOWNLOADED, &i ) )
495    {
496        tor->downloadedPrev = i;
497        fieldsLoaded |= TR_FR_DOWNLOADED;
498    }
499
500    if( ( fieldsToLoad & TR_FR_UPLOADED )
501      && tr_bencDictFindInt( &top, KEY_UPLOADED, &i ) )
502    {
503        tor->uploadedPrev = i;
504        fieldsLoaded |= TR_FR_UPLOADED;
505    }
506
507    if( ( fieldsToLoad & TR_FR_MAX_PEERS )
508      && tr_bencDictFindInt( &top, KEY_MAX_PEERS, &i ) )
509    {
510        tor->maxConnectedPeers = i;
511        fieldsLoaded |= TR_FR_MAX_PEERS;
512    }
513
514    if( ( fieldsToLoad & TR_FR_RUN )
515      && tr_bencDictFindInt( &top, KEY_PAUSED, &i ) )
516    {
517        tor->isRunning = i ? 0 : 1;
518        fieldsLoaded |= TR_FR_RUN;
519    }
520
521    if( ( fieldsToLoad & TR_FR_ADDED_DATE )
522      && tr_bencDictFindInt( &top, KEY_ADDED_DATE, &i ) )
523    {
524        tor->addedDate = i;
525        fieldsLoaded |= TR_FR_ADDED_DATE;
526    }
527
528    if( ( fieldsToLoad & TR_FR_DONE_DATE )
529      && tr_bencDictFindInt( &top, KEY_DONE_DATE, &i ) )
530    {
531        tor->doneDate = i;
532        fieldsLoaded |= TR_FR_DONE_DATE;
533    }
534
535    if( ( fieldsToLoad & TR_FR_ACTIVITY_DATE )
536      && tr_bencDictFindInt( &top, KEY_ACTIVITY_DATE, &i ) )
537    {
538        tor->activityDate = i;
539        fieldsLoaded |= TR_FR_ACTIVITY_DATE;
540    }
541
542    if( fieldsToLoad & TR_FR_PEERS )
543        fieldsLoaded |= loadPeers( &top, tor );
544
545    if( fieldsToLoad & TR_FR_PRIORITY )
546        fieldsLoaded |= loadPriorities( &top, tor );
547
548    if( fieldsToLoad & TR_FR_PROGRESS )
549        fieldsLoaded |= loadProgress( &top, tor );
550
551    if( fieldsToLoad & TR_FR_DND )
552        fieldsLoaded |= loadDND( &top, tor );
553
554    if( fieldsToLoad & TR_FR_SPEEDLIMIT )
555        fieldsLoaded |= loadSpeedLimits( &top, tor );
556
557    tr_bencFree( &top );
558    tr_free( filename );
559    return fieldsLoaded;
560}
561
562static uint64_t
563setFromCtor( tr_torrent *    tor,
564             uint64_t        fields,
565             const tr_ctor * ctor,
566             int             mode )
567{
568    uint64_t ret = 0;
569
570    if( fields & TR_FR_RUN )
571    {
572        uint8_t isPaused;
573        if( !tr_ctorGetPaused( ctor, mode, &isPaused ) )
574        {
575            tor->isRunning = !isPaused;
576            ret |= TR_FR_RUN;
577        }
578    }
579
580    if( fields & TR_FR_MAX_PEERS )
581        if( !tr_ctorGetPeerLimit( ctor, mode, &tor->maxConnectedPeers ) )
582            ret |= TR_FR_MAX_PEERS;
583
584    if( fields & TR_FR_DOWNLOAD_DIR )
585    {
586        const char * path;
587        if( !tr_ctorGetDownloadDir( ctor, mode, &path ) && path && *path )
588        {
589            ret |= TR_FR_DOWNLOAD_DIR;
590            tr_free( tor->downloadDir );
591            tor->downloadDir = tr_strdup( path );
592        }
593    }
594
595    return ret;
596}
597
598static uint64_t
599useManditoryFields( tr_torrent *    tor,
600                    uint64_t        fields,
601                    const tr_ctor * ctor )
602{
603    return setFromCtor( tor, fields, ctor, TR_FORCE );
604}
605
606static uint64_t
607useFallbackFields( tr_torrent *    tor,
608                   uint64_t        fields,
609                   const tr_ctor * ctor )
610{
611    return setFromCtor( tor, fields, ctor, TR_FALLBACK );
612}
613
614uint64_t
615tr_torrentLoadResume( tr_torrent *    tor,
616                      uint64_t        fieldsToLoad,
617                      const tr_ctor * ctor )
618{
619    uint64_t ret = 0;
620
621    ret |= useManditoryFields( tor, fieldsToLoad, ctor );
622    fieldsToLoad &= ~ret;
623    ret |= loadFromFile( tor, fieldsToLoad );
624    fieldsToLoad &= ~ret;
625    ret |= useFallbackFields( tor, fieldsToLoad, ctor );
626
627    return ret;
628}
629
630void
631tr_torrentRemoveResume( const tr_torrent * tor )
632{
633    char * filename = getResumeFilename( tor );
634
635    unlink( filename );
636    tr_fastResumeRemove( tor );
637    tr_free( filename );
638}
639
Note: See TracBrowser for help on using the repository browser.