source: trunk/libtransmission/resume.c @ 12262

Last change on this file since 12262 was 12262, checked in by jordan, 11 years ago

(trunk libT) handle situations where we don't know the bitfield's upper bound in advance. This comes up sometimes with magnet links.

  • Property svn:keywords set to Date Rev Author Id
File size: 27.1 KB
Line 
1/*
2 * This file Copyright (C) Mnemosyne LLC
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 12262 2011-03-30 04:14:57Z jordan $
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 "metainfo.h" /* tr_metainfoGetBasename() */
21#include "peer-mgr.h" /* pex */
22#include "platform.h" /* tr_getResumeDir() */
23#include "resume.h"
24#include "session.h"
25#include "torrent.h"
26#include "utils.h" /* tr_buildPath */
27
28#define KEY_ACTIVITY_DATE       "activity-date"
29#define KEY_ADDED_DATE          "added-date"
30#define KEY_CORRUPT             "corrupt"
31#define KEY_DONE_DATE           "done-date"
32#define KEY_DOWNLOAD_DIR        "destination"
33#define KEY_DND                 "dnd"
34#define KEY_DOWNLOADED          "downloaded"
35#define KEY_INCOMPLETE_DIR      "incomplete-dir"
36#define KEY_MAX_PEERS           "max-peers"
37#define KEY_PAUSED              "paused"
38#define KEY_PEERS               "peers2"
39#define KEY_PEERS6              "peers2-6"
40#define KEY_FILE_PRIORITIES     "priority"
41#define KEY_BANDWIDTH_PRIORITY  "bandwidth-priority"
42#define KEY_PROGRESS            "progress"
43#define KEY_SPEEDLIMIT_OLD      "speed-limit"
44#define KEY_SPEEDLIMIT_UP       "speed-limit-up"
45#define KEY_SPEEDLIMIT_DOWN     "speed-limit-down"
46#define KEY_RATIOLIMIT          "ratio-limit"
47#define KEY_IDLELIMIT           "idle-limit"
48#define KEY_UPLOADED            "uploaded"
49
50#define KEY_SPEED_KiBps            "speed"
51#define KEY_SPEED_Bps              "speed-Bps"
52#define KEY_USE_GLOBAL_SPEED_LIMIT "use-global-speed-limit"
53#define KEY_USE_SPEED_LIMIT        "use-speed-limit"
54#define KEY_TIME_SEEDING           "seeding-time-seconds"
55#define KEY_TIME_DOWNLOADING       "downloading-time-seconds"
56#define KEY_SPEEDLIMIT_DOWN_SPEED  "down-speed"
57#define KEY_SPEEDLIMIT_DOWN_MODE   "down-mode"
58#define KEY_SPEEDLIMIT_UP_SPEED    "up-speed"
59#define KEY_SPEEDLIMIT_UP_MODE     "up-mode"
60#define KEY_RATIOLIMIT_RATIO       "ratio-limit"
61#define KEY_RATIOLIMIT_MODE        "ratio-mode"
62#define KEY_IDLELIMIT_MINS         "idle-limit"
63#define KEY_IDLELIMIT_MODE         "idle-mode"
64
65#define KEY_PROGRESS_CHECKTIME "time-checked"
66#define KEY_PROGRESS_MTIMES    "mtimes"
67#define KEY_PROGRESS_BITFIELD  "bitfield"
68#define KEY_PROGRESS_BLOCKS    "blocks"
69#define KEY_PROGRESS_HAVE      "have"
70
71enum
72{
73    MAX_REMEMBERED_PEERS = 200
74};
75
76static char*
77getResumeFilename( const tr_torrent * tor )
78{
79    char * base = tr_metainfoGetBasename( tr_torrentInfo( tor ) );
80    char * filename = tr_strdup_printf( "%s" TR_PATH_DELIMITER_STR "%s.resume",
81                                        tr_getResumeDir( tor->session ), base );
82    tr_free( base );
83    return filename;
84}
85
86/***
87****
88***/
89
90static void
91savePeers( tr_benc * dict, const tr_torrent * tor )
92{
93    int count;
94    tr_pex * pex;
95
96    count = tr_peerMgrGetPeers( (tr_torrent*) tor, &pex, TR_AF_INET, TR_PEERS_INTERESTING, MAX_REMEMBERED_PEERS );
97    if( count > 0 )
98        tr_bencDictAddRaw( dict, KEY_PEERS, pex, sizeof( tr_pex ) * count );
99    tr_free( pex );
100
101    count = tr_peerMgrGetPeers( (tr_torrent*) tor, &pex, TR_AF_INET6, TR_PEERS_INTERESTING, MAX_REMEMBERED_PEERS );
102    if( count > 0 )
103        tr_bencDictAddRaw( dict, KEY_PEERS6, pex, sizeof( tr_pex ) * count );
104
105    tr_free( pex );
106}
107
108static int
109addPeers( tr_torrent * tor, const uint8_t * buf, int buflen )
110{
111    int i;
112    int numAdded = 0;
113    const int count = buflen / sizeof( tr_pex );
114
115    for( i=0; i<count && numAdded<MAX_REMEMBERED_PEERS; ++i )
116    {
117        tr_pex pex;
118        memcpy( &pex, buf + ( i * sizeof( tr_pex ) ), sizeof( tr_pex ) );
119        if( tr_isPex( &pex ) )
120        {
121            tr_peerMgrAddPex( tor, TR_PEER_FROM_RESUME, &pex, -1 );
122            ++numAdded;
123        }
124    }
125
126    return numAdded;
127}
128
129
130static uint64_t
131loadPeers( tr_benc * dict, tr_torrent * tor )
132{
133    uint64_t        ret = 0;
134    const uint8_t * str;
135    size_t          len;
136
137    if( tr_bencDictFindRaw( dict, KEY_PEERS, &str, &len ) )
138    {
139        const int numAdded = addPeers( tor, str, len );
140        tr_tordbg( tor, "Loaded %d IPv4 peers from resume file", numAdded );
141        ret = TR_FR_PEERS;
142    }
143
144    if( tr_bencDictFindRaw( dict, KEY_PEERS6, &str, &len ) )
145    {
146        const int numAdded = addPeers( tor, str, len );
147        tr_tordbg( tor, "Loaded %d IPv6 peers from resume file", numAdded );
148        ret = TR_FR_PEERS;
149    }
150
151    return ret;
152}
153
154/***
155****
156***/
157
158static void
159saveDND( tr_benc *          dict,
160         const tr_torrent * tor )
161{
162    const tr_info *       inf = tr_torrentInfo( tor );
163    const tr_file_index_t n = inf->fileCount;
164    tr_file_index_t       i;
165    tr_benc *             list;
166
167    list = tr_bencDictAddList( dict, KEY_DND, n );
168    for( i = 0; i < n; ++i )
169        tr_bencListAddInt( list, inf->files[i].dnd ? 1 : 0 );
170}
171
172static uint64_t
173loadDND( tr_benc *    dict,
174         tr_torrent * tor )
175{
176    uint64_t              ret = 0;
177    tr_info *             inf = &tor->info;
178    const tr_file_index_t n = inf->fileCount;
179    tr_benc *             list = NULL;
180
181    if( tr_bencDictFindList( dict, KEY_DND, &list )
182      && ( tr_bencListSize( list ) == n ) )
183    {
184        int64_t           tmp;
185        tr_file_index_t * dl = tr_new( tr_file_index_t, n );
186        tr_file_index_t * dnd = tr_new( tr_file_index_t, n );
187        tr_file_index_t   i, dlCount = 0, dndCount = 0;
188
189        for( i = 0; i < n; ++i )
190        {
191            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) && tmp )
192                dnd[dndCount++] = i;
193            else
194                dl[dlCount++] = i;
195        }
196
197        if( dndCount )
198        {
199            tr_torrentInitFileDLs ( tor, dnd, dndCount, false );
200            tr_tordbg( tor, "Resume file found %d files listed as dnd",
201                       dndCount );
202        }
203        if( dlCount )
204        {
205            tr_torrentInitFileDLs ( tor, dl, dlCount, true );
206            tr_tordbg( tor,
207                       "Resume file found %d files marked for download",
208                       dlCount );
209        }
210
211        tr_free( dnd );
212        tr_free( dl );
213        ret = TR_FR_DND;
214    }
215    else
216    {
217        tr_tordbg(
218            tor,
219            "Couldn't load DND flags. DND list (%p) has %zu children; torrent has %d files",
220            list, tr_bencListSize( list ), (int)n );
221    }
222
223    return ret;
224}
225
226/***
227****
228***/
229
230static void
231saveFilePriorities( tr_benc * dict, const tr_torrent * tor )
232{
233    const tr_info *       inf = tr_torrentInfo( tor );
234    const tr_file_index_t n = inf->fileCount;
235    tr_file_index_t       i;
236    tr_benc *             list;
237
238    list = tr_bencDictAddList( dict, KEY_FILE_PRIORITIES, n );
239    for( i = 0; i < n; ++i )
240        tr_bencListAddInt( list, inf->files[i].priority );
241}
242
243static uint64_t
244loadFilePriorities( tr_benc * dict, tr_torrent * tor )
245{
246    uint64_t              ret = 0;
247    tr_info *             inf = &tor->info;
248    const tr_file_index_t n = inf->fileCount;
249    tr_benc *             list;
250
251    if( tr_bencDictFindList( dict, KEY_FILE_PRIORITIES, &list )
252      && ( tr_bencListSize( list ) == n ) )
253    {
254        int64_t priority;
255        tr_file_index_t i;
256        for( i = 0; i < n; ++i )
257            if( tr_bencGetInt( tr_bencListChild( list, i ), &priority ) )
258                tr_torrentInitFilePriority( tor, i, priority );
259        ret = TR_FR_FILE_PRIORITIES;
260    }
261
262    return ret;
263}
264
265/***
266****
267***/
268
269static void
270saveSingleSpeedLimit( tr_benc * d, const tr_torrent * tor, tr_direction dir )
271{
272    tr_bencDictReserve( d, 3 );
273    tr_bencDictAddInt( d, KEY_SPEED_Bps, tr_torrentGetSpeedLimit_Bps( tor, dir ) );
274    tr_bencDictAddBool( d, KEY_USE_GLOBAL_SPEED_LIMIT, tr_torrentUsesSessionLimits( tor ) );
275    tr_bencDictAddBool( d, KEY_USE_SPEED_LIMIT, tr_torrentUsesSpeedLimit( tor, dir ) );
276}
277
278static void
279saveSpeedLimits( tr_benc * dict, const tr_torrent * tor )
280{
281    saveSingleSpeedLimit( tr_bencDictAddDict( dict, KEY_SPEEDLIMIT_DOWN, 0 ), tor, TR_DOWN );
282    saveSingleSpeedLimit( tr_bencDictAddDict( dict, KEY_SPEEDLIMIT_UP, 0 ), tor, TR_UP );
283}
284
285static void
286saveRatioLimits( tr_benc * dict, const tr_torrent * tor )
287{
288    tr_benc * d = tr_bencDictAddDict( dict, KEY_RATIOLIMIT, 2 );
289    tr_bencDictAddReal( d, KEY_RATIOLIMIT_RATIO, tr_torrentGetRatioLimit( tor ) );
290    tr_bencDictAddInt( d, KEY_RATIOLIMIT_MODE, tr_torrentGetRatioMode( tor ) );
291}
292
293static void
294saveIdleLimits( tr_benc * dict, const tr_torrent * tor )
295{
296    tr_benc * d = tr_bencDictAddDict( dict, KEY_IDLELIMIT, 2 );
297    tr_bencDictAddInt( d, KEY_IDLELIMIT_MINS, tr_torrentGetIdleLimit( tor ) );
298    tr_bencDictAddInt( d, KEY_IDLELIMIT_MODE, tr_torrentGetIdleMode( tor ) );
299}
300
301static void
302loadSingleSpeedLimit( tr_benc * d, tr_direction dir, tr_torrent * tor )
303{
304    int64_t i;
305    bool boolVal;
306
307    if( tr_bencDictFindInt( d, KEY_SPEED_Bps, &i ) )
308        tr_torrentSetSpeedLimit_Bps( tor, dir, i );
309    else if( tr_bencDictFindInt( d, KEY_SPEED_KiBps, &i ) )
310        tr_torrentSetSpeedLimit_Bps( tor, dir, i*1024 );
311
312    if( tr_bencDictFindBool( d, KEY_USE_SPEED_LIMIT, &boolVal ) )
313        tr_torrentUseSpeedLimit( tor, dir, boolVal );
314
315    if( tr_bencDictFindBool( d, KEY_USE_GLOBAL_SPEED_LIMIT, &boolVal ) )
316        tr_torrentUseSessionLimits( tor, boolVal );
317}
318
319enum old_speed_modes
320{
321    TR_SPEEDLIMIT_GLOBAL,   /* only follow the overall speed limit */
322    TR_SPEEDLIMIT_SINGLE    /* only follow the per-torrent limit */
323};
324
325static uint64_t
326loadSpeedLimits( tr_benc * dict, tr_torrent * tor )
327{
328    uint64_t  ret = 0;
329    tr_benc * d;
330
331    if( tr_bencDictFindDict( dict, KEY_SPEEDLIMIT_UP, &d ) )
332    {
333        loadSingleSpeedLimit( d, TR_UP, tor );
334        ret = TR_FR_SPEEDLIMIT;
335    }
336    if( tr_bencDictFindDict( dict, KEY_SPEEDLIMIT_DOWN, &d ) )
337    {
338        loadSingleSpeedLimit( d, TR_DOWN, tor );
339        ret = TR_FR_SPEEDLIMIT;
340    }
341
342    /* older speedlimit structure */
343    if( !ret && tr_bencDictFindDict( dict, KEY_SPEEDLIMIT_OLD, &d ) )
344    {
345
346        int64_t i;
347        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_DOWN_SPEED, &i ) )
348            tr_torrentSetSpeedLimit_Bps( tor, TR_DOWN, i*1024 );
349        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_DOWN_MODE, &i ) ) {
350            tr_torrentUseSpeedLimit( tor, TR_DOWN, i==TR_SPEEDLIMIT_SINGLE );
351            tr_torrentUseSessionLimits( tor, i==TR_SPEEDLIMIT_GLOBAL );
352         }
353        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_UP_SPEED, &i ) )
354            tr_torrentSetSpeedLimit_Bps( tor, TR_UP, i*1024 );
355        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_UP_MODE, &i ) ) {
356            tr_torrentUseSpeedLimit( tor, TR_UP, i==TR_SPEEDLIMIT_SINGLE );
357            tr_torrentUseSessionLimits( tor, i==TR_SPEEDLIMIT_GLOBAL );
358        }
359        ret = TR_FR_SPEEDLIMIT;
360    }
361
362    return ret;
363}
364
365static uint64_t
366loadRatioLimits( tr_benc *    dict,
367                 tr_torrent * tor )
368{
369    uint64_t  ret = 0;
370    tr_benc * d;
371
372    if( tr_bencDictFindDict( dict, KEY_RATIOLIMIT, &d ) )
373    {
374        int64_t i;
375        double dratio;
376        if( tr_bencDictFindReal( d, KEY_RATIOLIMIT_RATIO, &dratio ) )
377            tr_torrentSetRatioLimit( tor, dratio );
378        if( tr_bencDictFindInt( d, KEY_RATIOLIMIT_MODE, &i ) )
379            tr_torrentSetRatioMode( tor, i );
380      ret = TR_FR_RATIOLIMIT;
381    }
382
383    return ret;
384}
385
386static uint64_t
387loadIdleLimits( tr_benc *    dict,
388                      tr_torrent * tor )
389{
390    uint64_t  ret = 0;
391    tr_benc * d;
392
393    if( tr_bencDictFindDict( dict, KEY_IDLELIMIT, &d ) )
394    {
395        int64_t i;
396        int64_t imin;
397        if( tr_bencDictFindInt( d, KEY_IDLELIMIT_MINS, &imin ) )
398            tr_torrentSetIdleLimit( tor, imin );
399        if( tr_bencDictFindInt( d, KEY_IDLELIMIT_MODE, &i ) )
400            tr_torrentSetIdleMode( tor, i );
401      ret = TR_FR_IDLELIMIT;
402    }
403
404    return ret;
405}
406/***
407****
408***/
409
410static void
411bitfieldToBenc( const tr_bitfield * b, tr_benc * benc )
412{
413    if( tr_bitfieldHasAll( b ) )
414        tr_bencInitStr( benc, "all", 3 );
415    else if( tr_bitfieldHasNone( b ) )
416        tr_bencInitStr( benc, "none", 4 );
417    else {
418        size_t byte_count = 0;
419        uint8_t * raw = tr_bitfieldGetRaw( b, &byte_count );
420        tr_bencInitRaw( benc, raw, byte_count );
421        tr_free( raw );
422    }
423}
424
425
426static void
427saveProgress( tr_benc * dict, tr_torrent * tor )
428{
429    tr_benc * l;
430    tr_benc * prog;
431    tr_file_index_t fi;
432    const tr_info * inf = tr_torrentInfo( tor );
433    const time_t now = tr_time( );
434
435    prog = tr_bencDictAddDict( dict, KEY_PROGRESS, 3 );
436
437    /* add the file/piece check timestamps... */
438    l = tr_bencDictAddList( prog, KEY_PROGRESS_CHECKTIME, inf->fileCount );
439    for( fi=0; fi<inf->fileCount; ++fi )
440    {
441        const tr_piece * p;
442        const tr_piece * pend;
443        time_t oldest_nonzero = now;
444        time_t newest = 0;
445        bool has_zero = false;
446        const time_t mtime = tr_torrentGetFileMTime( tor, fi );
447        const tr_file * f = &inf->files[fi];
448
449        /* get the oldest and newest nonzero timestamps for pieces in this file */
450        for( p=&inf->pieces[f->firstPiece], pend=&inf->pieces[f->lastPiece]; p!=pend; ++p )
451        {
452            if( !p->timeChecked )
453                has_zero = true;
454            else if( oldest_nonzero > p->timeChecked )
455                oldest_nonzero = p->timeChecked;
456            if( newest < p->timeChecked )
457                newest = p->timeChecked;
458        }
459
460        /* If some of a file's pieces have been checked more recently than
461           the file's mtime, and some lest recently, then that file will
462           have a list containing timestamps for each piece.
463
464           However, the most common use case is that the file doesn't change
465           after it's downloaded. To reduce overhead in the .resume file,
466           only a single timestamp is saved for the file if *all* or *none*
467           of the pieces were tested more recently than the file's mtime. */
468
469        if( !has_zero && ( mtime <= oldest_nonzero ) ) /* all checked */
470            tr_bencListAddInt( l, oldest_nonzero );
471        else if( newest < mtime ) /* none checked */
472            tr_bencListAddInt( l, newest );
473        else { /* some are checked, some aren't... so list piece by piece */
474            const int offset = oldest_nonzero - 1;
475            tr_benc * ll = tr_bencListAddList( l, 2 + f->lastPiece - f->firstPiece );
476            tr_bencListAddInt( ll, offset );
477            for( p=&inf->pieces[f->firstPiece], pend=&inf->pieces[f->lastPiece]+1; p!=pend; ++p )
478                tr_bencListAddInt( ll, p->timeChecked ? p->timeChecked - offset : 0 );
479        }
480    }
481
482    /* add the progress */
483    if( tor->completeness == TR_SEED )
484        tr_bencDictAddStr( prog, KEY_PROGRESS_HAVE, "all" );
485
486    /* add the blocks bitfield */
487    bitfieldToBenc( &tor->completion.blockBitfield,
488                    tr_bencDictAdd( prog, KEY_PROGRESS_BLOCKS ) );
489}
490
491static uint64_t
492loadProgress( tr_benc * dict, tr_torrent * tor )
493{
494    size_t i, n;
495    uint64_t ret = 0;
496    tr_benc * prog;
497    const tr_info * inf = tr_torrentInfo( tor );
498
499    for( i=0, n=inf->pieceCount; i<n; ++i )
500        inf->pieces[i].timeChecked = 0;
501
502    if( tr_bencDictFindDict( dict, KEY_PROGRESS, &prog ) )
503    {
504        const char * err;
505        const char * str;
506        const uint8_t * raw;
507        size_t rawlen;
508        tr_benc * l;
509        tr_benc * b;
510        struct tr_bitfield blocks = TR_BITFIELD_INIT;
511
512        if( tr_bencDictFindList( prog, KEY_PROGRESS_CHECKTIME, &l ) )
513        {
514            /* per-piece timestamps were added in 2.20.
515
516               If some of a file's pieces have been checked more recently than
517               the file's mtime, and some lest recently, then that file will
518               have a list containing timestamps for each piece.
519
520               However, the most common use case is that the file doesn't change
521               after it's downloaded. To reduce overhead in the .resume file,
522               only a single timestamp is saved for the file if *all* or *none*
523               of the pieces were tested more recently than the file's mtime. */
524
525            tr_file_index_t fi;
526
527            for( fi=0; fi<inf->fileCount; ++fi )
528            {
529                tr_benc * b = tr_bencListChild( l, fi );
530                const tr_file * f = &inf->files[fi];
531                tr_piece * p = &inf->pieces[f->firstPiece];
532                const tr_piece * pend = &inf->pieces[f->lastPiece]+1;
533
534                if( tr_bencIsInt( b ) )
535                {
536                    int64_t t;
537                    tr_bencGetInt( b, &t );
538                    for( ; p!=pend; ++p )
539                        p->timeChecked = (time_t)t;
540                }
541                else if( tr_bencIsList( b ) )
542                {
543                    int i = 0;
544                    int64_t offset = 0;
545                    const int pieces = f->lastPiece + 1 - f->firstPiece;
546
547                    tr_bencGetInt( tr_bencListChild( b, 0 ), &offset );
548
549                    for( i=0; i<pieces; ++i )
550                    {
551                        int64_t t = 0;
552                        tr_bencGetInt( tr_bencListChild( b, i+1 ), &t );
553                        inf->pieces[f->firstPiece+i].timeChecked = (time_t)(t ? t + offset : 0);
554                    }
555                }
556            }
557        }
558        else if( tr_bencDictFindList( prog, KEY_PROGRESS_MTIMES, &l ) )
559        {
560            tr_file_index_t fi;
561
562            /* Before 2.20, we stored the files' mtimes in the .resume file.
563               When loading the .resume file, a torrent's file would be flagged
564               as untested if its stored mtime didn't match its real mtime. */
565
566            for( fi=0; fi<inf->fileCount; ++fi )
567            {
568                int64_t t;
569
570                if( tr_bencGetInt( tr_bencListChild( l, fi ), &t ) )
571                {
572                    const tr_file * f = &inf->files[fi];
573                    tr_piece * p = &inf->pieces[f->firstPiece];
574                    const tr_piece * pend = &inf->pieces[f->lastPiece];
575                    const time_t mtime = tr_torrentGetFileMTime( tor, fi );
576                    const time_t timeChecked = mtime==t ? mtime : 0;
577
578                    for( ; p!=pend; ++p )
579                        p->timeChecked = timeChecked;
580                }
581            }
582        }
583
584        err = NULL;
585        tr_bitfieldConstruct( &blocks, tor->blockCount );
586
587        if(( b = tr_bencDictFind( prog, KEY_PROGRESS_BLOCKS )))
588        {
589            size_t buflen;
590            const uint8_t * buf;
591
592            if( !tr_bencGetRaw( b, &buf, &buflen ) )
593                err = "Invalid value for \"blocks\"";
594            else if( ( buflen == 3 ) && !memcmp( buf, "all", 3 ) )
595                tr_bitfieldSetHasAll( &blocks );
596            else if( ( buflen == 4 ) && !memcmp( buf, "none", 4 ) )
597                tr_bitfieldSetHasNone( &blocks );
598            else
599                tr_bitfieldSetRaw( &blocks, buf, buflen );
600        }
601        else if( tr_bencDictFindStr( prog, KEY_PROGRESS_HAVE, &str ) )
602        {
603            if( !strcmp( str, "all" ) )
604                tr_bitfieldSetHasAll( &blocks );
605            else
606                err = "Invalid value for HAVE";
607        }
608        else if( tr_bencDictFindRaw( prog, KEY_PROGRESS_BITFIELD, &raw, &rawlen ) )
609        {
610            tr_bitfieldSetRaw( &blocks, raw, rawlen );
611        }
612        else err = "Couldn't find 'pieces' or 'have' or 'bitfield'";
613
614        if( err != NULL )
615            tr_tordbg( tor, "Torrent needs to be verified - %s", err );
616        else
617            tr_cpBlockInit( &tor->completion, &blocks );
618
619        tr_bitfieldDestruct( &blocks );
620        ret = TR_FR_PROGRESS;
621    }
622
623    return ret;
624}
625
626/***
627****
628***/
629
630void
631tr_torrentSaveResume( tr_torrent * tor )
632{
633    int err;
634    tr_benc top;
635    char * filename;
636
637    if( !tr_isTorrent( tor ) )
638        return;
639
640    tr_bencInitDict( &top, 50 ); /* arbitrary "big enough" number */
641    tr_bencDictAddInt( &top, KEY_TIME_SEEDING, tor->secondsSeeding );
642    tr_bencDictAddInt( &top, KEY_TIME_DOWNLOADING, tor->secondsDownloading );
643    tr_bencDictAddInt( &top, KEY_ACTIVITY_DATE, tor->activityDate );
644    tr_bencDictAddInt( &top, KEY_ADDED_DATE, tor->addedDate );
645    tr_bencDictAddInt( &top, KEY_CORRUPT, tor->corruptPrev + tor->corruptCur );
646    tr_bencDictAddInt( &top, KEY_DONE_DATE, tor->doneDate );
647    tr_bencDictAddStr( &top, KEY_DOWNLOAD_DIR, tor->downloadDir );
648    if( tor->incompleteDir != NULL )
649        tr_bencDictAddStr( &top, KEY_INCOMPLETE_DIR, tor->incompleteDir );
650    tr_bencDictAddInt( &top, KEY_DOWNLOADED, tor->downloadedPrev + tor->downloadedCur );
651    tr_bencDictAddInt( &top, KEY_UPLOADED, tor->uploadedPrev + tor->uploadedCur );
652    tr_bencDictAddInt( &top, KEY_MAX_PEERS, tor->maxConnectedPeers );
653    tr_bencDictAddInt( &top, KEY_BANDWIDTH_PRIORITY, tr_torrentGetPriority( tor ) );
654    tr_bencDictAddBool( &top, KEY_PAUSED, !tor->isRunning );
655    savePeers( &top, tor );
656    if( tr_torrentHasMetadata( tor ) )
657    {
658        saveFilePriorities( &top, tor );
659        saveDND( &top, tor );
660        saveProgress( &top, tor );
661    }
662    saveSpeedLimits( &top, tor );
663    saveRatioLimits( &top, tor );
664    saveIdleLimits( &top, tor );
665
666    filename = getResumeFilename( tor );
667    if(( err = tr_bencToFile( &top, TR_FMT_BENC, filename )))
668        tr_torrentSetLocalError( tor, "Unable to save resume file: %s", tr_strerror( err ) );
669    tr_free( filename );
670
671    tr_bencFree( &top );
672}
673
674static uint64_t
675loadFromFile( tr_torrent * tor,
676              uint64_t     fieldsToLoad )
677{
678    int64_t  i;
679    const char * str;
680    char * filename;
681    tr_benc top;
682    bool boolVal;
683    uint64_t fieldsLoaded = 0;
684    const bool wasDirty = tor->isDirty;
685
686    assert( tr_isTorrent( tor ) );
687
688    filename = getResumeFilename( tor );
689
690    if( tr_bencLoadFile( &top, TR_FMT_BENC, filename ) )
691    {
692        tr_tordbg( tor, "Couldn't read \"%s\"", filename );
693
694        tr_free( filename );
695        return fieldsLoaded;
696    }
697
698    tr_tordbg( tor, "Read resume file \"%s\"", filename );
699
700    if( ( fieldsToLoad & TR_FR_CORRUPT )
701      && tr_bencDictFindInt( &top, KEY_CORRUPT, &i ) )
702    {
703        tor->corruptPrev = i;
704        fieldsLoaded |= TR_FR_CORRUPT;
705    }
706
707    if( ( fieldsToLoad & ( TR_FR_PROGRESS | TR_FR_DOWNLOAD_DIR ) )
708      && ( tr_bencDictFindStr( &top, KEY_DOWNLOAD_DIR, &str ) )
709      && ( str && *str ) )
710    {
711        tr_free( tor->downloadDir );
712        tor->downloadDir = tr_strdup( str );
713        fieldsLoaded |= TR_FR_DOWNLOAD_DIR;
714    }
715
716    if( ( fieldsToLoad & ( TR_FR_PROGRESS | TR_FR_INCOMPLETE_DIR ) )
717      && ( tr_bencDictFindStr( &top, KEY_INCOMPLETE_DIR, &str ) )
718      && ( str && *str ) )
719    {
720        tr_free( tor->incompleteDir );
721        tor->incompleteDir = tr_strdup( str );
722        fieldsLoaded |= TR_FR_INCOMPLETE_DIR;
723    }
724
725    if( ( fieldsToLoad & TR_FR_DOWNLOADED )
726      && tr_bencDictFindInt( &top, KEY_DOWNLOADED, &i ) )
727    {
728        tor->downloadedPrev = i;
729        fieldsLoaded |= TR_FR_DOWNLOADED;
730    }
731
732    if( ( fieldsToLoad & TR_FR_UPLOADED )
733      && tr_bencDictFindInt( &top, KEY_UPLOADED, &i ) )
734    {
735        tor->uploadedPrev = i;
736        fieldsLoaded |= TR_FR_UPLOADED;
737    }
738
739    if( ( fieldsToLoad & TR_FR_MAX_PEERS )
740      && tr_bencDictFindInt( &top, KEY_MAX_PEERS, &i ) )
741    {
742        tor->maxConnectedPeers = i;
743        fieldsLoaded |= TR_FR_MAX_PEERS;
744    }
745
746    if( ( fieldsToLoad & TR_FR_RUN )
747      && tr_bencDictFindBool( &top, KEY_PAUSED, &boolVal ) )
748    {
749        tor->isRunning = !boolVal;
750        fieldsLoaded |= TR_FR_RUN;
751    }
752
753    if( ( fieldsToLoad & TR_FR_ADDED_DATE )
754      && tr_bencDictFindInt( &top, KEY_ADDED_DATE, &i ) )
755    {
756        tor->addedDate = i;
757        fieldsLoaded |= TR_FR_ADDED_DATE;
758    }
759
760    if( ( fieldsToLoad & TR_FR_DONE_DATE )
761      && tr_bencDictFindInt( &top, KEY_DONE_DATE, &i ) )
762    {
763        tor->doneDate = i;
764        fieldsLoaded |= TR_FR_DONE_DATE;
765    }
766
767    if( ( fieldsToLoad & TR_FR_ACTIVITY_DATE )
768      && tr_bencDictFindInt( &top, KEY_ACTIVITY_DATE, &i ) )
769    {
770        tr_torrentSetActivityDate( tor, i );
771        fieldsLoaded |= TR_FR_ACTIVITY_DATE;
772    }
773
774    if( ( fieldsToLoad & TR_FR_TIME_SEEDING )
775      && tr_bencDictFindInt( &top, KEY_TIME_SEEDING, &i ) )
776    {
777        tor->secondsSeeding = i;
778        fieldsLoaded |= TR_FR_TIME_SEEDING;
779    }
780
781    if( ( fieldsToLoad & TR_FR_TIME_DOWNLOADING )
782      && tr_bencDictFindInt( &top, KEY_TIME_DOWNLOADING, &i ) )
783    {
784        tor->secondsDownloading = i;
785        fieldsLoaded |= TR_FR_TIME_DOWNLOADING;
786    }
787
788    if( ( fieldsToLoad & TR_FR_BANDWIDTH_PRIORITY )
789      && tr_bencDictFindInt( &top, KEY_BANDWIDTH_PRIORITY, &i )
790      && tr_isPriority( i ) )
791    {
792        tr_torrentSetPriority( tor, i );
793        fieldsLoaded |= TR_FR_BANDWIDTH_PRIORITY;
794    }
795
796    if( fieldsToLoad & TR_FR_PEERS )
797        fieldsLoaded |= loadPeers( &top, tor );
798
799    if( fieldsToLoad & TR_FR_FILE_PRIORITIES )
800        fieldsLoaded |= loadFilePriorities( &top, tor );
801
802    if( fieldsToLoad & TR_FR_PROGRESS )
803        fieldsLoaded |= loadProgress( &top, tor );
804
805    if( fieldsToLoad & TR_FR_DND )
806        fieldsLoaded |= loadDND( &top, tor );
807
808    if( fieldsToLoad & TR_FR_SPEEDLIMIT )
809        fieldsLoaded |= loadSpeedLimits( &top, tor );
810
811    if( fieldsToLoad & TR_FR_RATIOLIMIT )
812        fieldsLoaded |= loadRatioLimits( &top, tor );
813
814    if( fieldsToLoad & TR_FR_IDLELIMIT )
815        fieldsLoaded |= loadIdleLimits( &top, tor );
816
817    /* loading the resume file triggers of a lot of changes,
818     * but none of them needs to trigger a re-saving of the
819     * same resume information... */
820    tor->isDirty = wasDirty;
821
822    tr_bencFree( &top );
823    tr_free( filename );
824    return fieldsLoaded;
825}
826
827static uint64_t
828setFromCtor( tr_torrent *    tor,
829             uint64_t        fields,
830             const tr_ctor * ctor,
831             int             mode )
832{
833    uint64_t ret = 0;
834
835    if( fields & TR_FR_RUN )
836    {
837        bool isPaused;
838        if( !tr_ctorGetPaused( ctor, mode, &isPaused ) )
839        {
840            tor->isRunning = !isPaused;
841            ret |= TR_FR_RUN;
842        }
843    }
844
845    if( fields & TR_FR_MAX_PEERS )
846        if( !tr_ctorGetPeerLimit( ctor, mode, &tor->maxConnectedPeers ) )
847            ret |= TR_FR_MAX_PEERS;
848
849    if( fields & TR_FR_DOWNLOAD_DIR )
850    {
851        const char * path;
852        if( !tr_ctorGetDownloadDir( ctor, mode, &path ) && path && *path )
853        {
854            ret |= TR_FR_DOWNLOAD_DIR;
855            tr_free( tor->downloadDir );
856            tor->downloadDir = tr_strdup( path );
857        }
858    }
859
860    return ret;
861}
862
863static uint64_t
864useManditoryFields( tr_torrent *    tor,
865                    uint64_t        fields,
866                    const tr_ctor * ctor )
867{
868    return setFromCtor( tor, fields, ctor, TR_FORCE );
869}
870
871static uint64_t
872useFallbackFields( tr_torrent *    tor,
873                   uint64_t        fields,
874                   const tr_ctor * ctor )
875{
876    return setFromCtor( tor, fields, ctor, TR_FALLBACK );
877}
878
879uint64_t
880tr_torrentLoadResume( tr_torrent *    tor,
881                      uint64_t        fieldsToLoad,
882                      const tr_ctor * ctor )
883{
884    uint64_t ret = 0;
885
886    assert( tr_isTorrent( tor ) );
887
888    ret |= useManditoryFields( tor, fieldsToLoad, ctor );
889    fieldsToLoad &= ~ret;
890    ret |= loadFromFile( tor, fieldsToLoad );
891    fieldsToLoad &= ~ret;
892    ret |= useFallbackFields( tor, fieldsToLoad, ctor );
893
894    return ret;
895}
896
897void
898tr_torrentRemoveResume( const tr_torrent * tor )
899{
900    char * filename = getResumeFilename( tor );
901    unlink( filename );
902    tr_free( filename );
903}
904
Note: See TracBrowser for help on using the repository browser.