source: trunk/libtransmission/resume.c @ 12254

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

(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.

  • 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 12254 2011-03-29 15:23:54Z 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        tr_bencInitRaw( benc, b->bits, b->byte_count );
419}
420
421
422static void
423saveProgress( tr_benc * dict, tr_torrent * tor )
424{
425    tr_benc * l;
426    tr_benc * prog;
427    tr_file_index_t fi;
428    const tr_info * inf = tr_torrentInfo( tor );
429    const time_t now = tr_time( );
430
431    prog = tr_bencDictAddDict( dict, KEY_PROGRESS, 3 );
432
433    /* add the file/piece check timestamps... */
434    l = tr_bencDictAddList( prog, KEY_PROGRESS_CHECKTIME, inf->fileCount );
435    for( fi=0; fi<inf->fileCount; ++fi )
436    {
437        const tr_piece * p;
438        const tr_piece * pend;
439        time_t oldest_nonzero = now;
440        time_t newest = 0;
441        bool has_zero = false;
442        const time_t mtime = tr_torrentGetFileMTime( tor, fi );
443        const tr_file * f = &inf->files[fi];
444
445        /* get the oldest and newest nonzero timestamps for pieces in this file */
446        for( p=&inf->pieces[f->firstPiece], pend=&inf->pieces[f->lastPiece]; p!=pend; ++p )
447        {
448            if( !p->timeChecked )
449                has_zero = true;
450            else if( oldest_nonzero > p->timeChecked )
451                oldest_nonzero = p->timeChecked;
452            if( newest < p->timeChecked )
453                newest = p->timeChecked;
454        }
455
456        /* If some of a file's pieces have been checked more recently than
457           the file's mtime, and some lest recently, then that file will
458           have a list containing timestamps for each piece.
459
460           However, the most common use case is that the file doesn't change
461           after it's downloaded. To reduce overhead in the .resume file,
462           only a single timestamp is saved for the file if *all* or *none*
463           of the pieces were tested more recently than the file's mtime. */
464
465        if( !has_zero && ( mtime <= oldest_nonzero ) ) /* all checked */
466            tr_bencListAddInt( l, oldest_nonzero );
467        else if( newest < mtime ) /* none checked */
468            tr_bencListAddInt( l, newest );
469        else { /* some are checked, some aren't... so list piece by piece */
470            const int offset = oldest_nonzero - 1;
471            tr_benc * ll = tr_bencListAddList( l, 2 + f->lastPiece - f->firstPiece );
472            tr_bencListAddInt( ll, offset );
473            for( p=&inf->pieces[f->firstPiece], pend=&inf->pieces[f->lastPiece]+1; p!=pend; ++p )
474                tr_bencListAddInt( ll, p->timeChecked ? p->timeChecked - offset : 0 );
475        }
476    }
477
478    /* add the progress */
479    if( tor->completeness == TR_SEED )
480        tr_bencDictAddStr( prog, KEY_PROGRESS_HAVE, "all" );
481
482    /* add the blocks bitfield */
483    bitfieldToBenc( &tor->completion.blockBitfield,
484                    tr_bencDictAdd( prog, KEY_PROGRESS_BLOCKS ) );
485}
486
487static uint64_t
488loadProgress( tr_benc * dict, tr_torrent * tor )
489{
490    size_t i, n;
491    uint64_t ret = 0;
492    tr_benc * prog;
493    const tr_info * inf = tr_torrentInfo( tor );
494
495    for( i=0, n=inf->pieceCount; i<n; ++i )
496        inf->pieces[i].timeChecked = 0;
497
498    if( tr_bencDictFindDict( dict, KEY_PROGRESS, &prog ) )
499    {
500        const char * err;
501        const char * str;
502        const uint8_t * raw;
503        size_t rawlen;
504        tr_benc * l;
505        tr_benc * b;
506        struct tr_bitfield blocks = TR_BITFIELD_INIT;
507
508        if( tr_bencDictFindList( prog, KEY_PROGRESS_CHECKTIME, &l ) )
509        {
510            /* per-piece timestamps were added in 2.20.
511
512               If some of a file's pieces have been checked more recently than
513               the file's mtime, and some lest recently, then that file will
514               have a list containing timestamps for each piece.
515
516               However, the most common use case is that the file doesn't change
517               after it's downloaded. To reduce overhead in the .resume file,
518               only a single timestamp is saved for the file if *all* or *none*
519               of the pieces were tested more recently than the file's mtime. */
520
521            tr_file_index_t fi;
522
523            for( fi=0; fi<inf->fileCount; ++fi )
524            {
525                tr_benc * b = tr_bencListChild( l, fi );
526                const tr_file * f = &inf->files[fi];
527                tr_piece * p = &inf->pieces[f->firstPiece];
528                const tr_piece * pend = &inf->pieces[f->lastPiece]+1;
529
530                if( tr_bencIsInt( b ) )
531                {
532                    int64_t t;
533                    tr_bencGetInt( b, &t );
534                    for( ; p!=pend; ++p )
535                        p->timeChecked = (time_t)t;
536                }
537                else if( tr_bencIsList( b ) )
538                {
539                    int i = 0;
540                    int64_t offset = 0;
541                    const int pieces = f->lastPiece + 1 - f->firstPiece;
542
543                    tr_bencGetInt( tr_bencListChild( b, 0 ), &offset );
544
545                    for( i=0; i<pieces; ++i )
546                    {
547                        int64_t t = 0;
548                        tr_bencGetInt( tr_bencListChild( b, i+1 ), &t );
549                        inf->pieces[f->firstPiece+i].timeChecked = (time_t)(t ? t + offset : 0);
550                    }
551                }
552            }
553        }
554        else if( tr_bencDictFindList( prog, KEY_PROGRESS_MTIMES, &l ) )
555        {
556            tr_file_index_t fi;
557
558            /* Before 2.20, we stored the files' mtimes in the .resume file.
559               When loading the .resume file, a torrent's file would be flagged
560               as untested if its stored mtime didn't match its real mtime. */
561
562            for( fi=0; fi<inf->fileCount; ++fi )
563            {
564                int64_t t;
565
566                if( tr_bencGetInt( tr_bencListChild( l, fi ), &t ) )
567                {
568                    const tr_file * f = &inf->files[fi];
569                    tr_piece * p = &inf->pieces[f->firstPiece];
570                    const tr_piece * pend = &inf->pieces[f->lastPiece];
571                    const time_t mtime = tr_torrentGetFileMTime( tor, fi );
572                    const time_t timeChecked = mtime==t ? mtime : 0;
573
574                    for( ; p!=pend; ++p )
575                        p->timeChecked = timeChecked;
576                }
577            }
578        }
579
580        err = NULL;
581        tr_bitfieldConstruct( &blocks, tor->blockCount );
582
583        if(( b = tr_bencDictFind( prog, KEY_PROGRESS_BLOCKS )))
584        {
585            size_t buflen;
586            const uint8_t * buf;
587
588            if( !tr_bencGetRaw( b, &buf, &buflen ) )
589                err = "Invalid value for \"blocks\"";
590            else if( ( buflen == 3 ) && !memcmp( buf, "all", 3 ) )
591                tr_bitfieldSetHasAll( &blocks );
592            else if( ( buflen == 4 ) && !memcmp( buf, "none", 4 ) )
593                tr_bitfieldSetHasNone( &blocks );
594            else
595                tr_bitfieldSetRaw( &blocks, buf, buflen );
596        }
597        else if( tr_bencDictFindStr( prog, KEY_PROGRESS_HAVE, &str ) )
598        {
599            if( !strcmp( str, "all" ) )
600                tr_bitfieldSetHasAll( &blocks );
601            else
602                err = "Invalid value for HAVE";
603        }
604        else if( tr_bencDictFindRaw( prog, KEY_PROGRESS_BITFIELD, &raw, &rawlen ) )
605        {
606            tr_bitfieldSetRaw( &blocks, raw, rawlen );
607        }
608        else err = "Couldn't find 'pieces' or 'have' or 'bitfield'";
609
610        if( !err && !tr_cpBlockInit( &tor->completion, &blocks ) )
611            err = "Error loading bitfield";
612        if( err != NULL )
613            tr_tordbg( tor, "Torrent needs to be verified - %s", err );
614
615        tr_bitfieldDestruct( &blocks );
616        ret = TR_FR_PROGRESS;
617    }
618
619    return ret;
620}
621
622/***
623****
624***/
625
626void
627tr_torrentSaveResume( tr_torrent * tor )
628{
629    int err;
630    tr_benc top;
631    char * filename;
632
633    if( !tr_isTorrent( tor ) )
634        return;
635
636    tr_bencInitDict( &top, 50 ); /* arbitrary "big enough" number */
637    tr_bencDictAddInt( &top, KEY_TIME_SEEDING, tor->secondsSeeding );
638    tr_bencDictAddInt( &top, KEY_TIME_DOWNLOADING, tor->secondsDownloading );
639    tr_bencDictAddInt( &top, KEY_ACTIVITY_DATE, tor->activityDate );
640    tr_bencDictAddInt( &top, KEY_ADDED_DATE, tor->addedDate );
641    tr_bencDictAddInt( &top, KEY_CORRUPT, tor->corruptPrev + tor->corruptCur );
642    tr_bencDictAddInt( &top, KEY_DONE_DATE, tor->doneDate );
643    tr_bencDictAddStr( &top, KEY_DOWNLOAD_DIR, tor->downloadDir );
644    if( tor->incompleteDir != NULL )
645        tr_bencDictAddStr( &top, KEY_INCOMPLETE_DIR, tor->incompleteDir );
646    tr_bencDictAddInt( &top, KEY_DOWNLOADED, tor->downloadedPrev + tor->downloadedCur );
647    tr_bencDictAddInt( &top, KEY_UPLOADED, tor->uploadedPrev + tor->uploadedCur );
648    tr_bencDictAddInt( &top, KEY_MAX_PEERS, tor->maxConnectedPeers );
649    tr_bencDictAddInt( &top, KEY_BANDWIDTH_PRIORITY, tr_torrentGetPriority( tor ) );
650    tr_bencDictAddBool( &top, KEY_PAUSED, !tor->isRunning );
651    savePeers( &top, tor );
652    if( tr_torrentHasMetadata( tor ) )
653    {
654        saveFilePriorities( &top, tor );
655        saveDND( &top, tor );
656        saveProgress( &top, tor );
657    }
658    saveSpeedLimits( &top, tor );
659    saveRatioLimits( &top, tor );
660    saveIdleLimits( &top, tor );
661
662    filename = getResumeFilename( tor );
663    if(( err = tr_bencToFile( &top, TR_FMT_BENC, filename )))
664        tr_torrentSetLocalError( tor, "Unable to save resume file: %s", tr_strerror( err ) );
665    tr_free( filename );
666
667    tr_bencFree( &top );
668}
669
670static uint64_t
671loadFromFile( tr_torrent * tor,
672              uint64_t     fieldsToLoad )
673{
674    int64_t  i;
675    const char * str;
676    char * filename;
677    tr_benc top;
678    bool boolVal;
679    uint64_t fieldsLoaded = 0;
680    const bool wasDirty = tor->isDirty;
681
682    assert( tr_isTorrent( tor ) );
683
684    filename = getResumeFilename( tor );
685
686    if( tr_bencLoadFile( &top, TR_FMT_BENC, filename ) )
687    {
688        tr_tordbg( tor, "Couldn't read \"%s\"", filename );
689
690        tr_free( filename );
691        return fieldsLoaded;
692    }
693
694    tr_tordbg( tor, "Read resume file \"%s\"", filename );
695
696    if( ( fieldsToLoad & TR_FR_CORRUPT )
697      && tr_bencDictFindInt( &top, KEY_CORRUPT, &i ) )
698    {
699        tor->corruptPrev = i;
700        fieldsLoaded |= TR_FR_CORRUPT;
701    }
702
703    if( ( fieldsToLoad & ( TR_FR_PROGRESS | TR_FR_DOWNLOAD_DIR ) )
704      && ( tr_bencDictFindStr( &top, KEY_DOWNLOAD_DIR, &str ) )
705      && ( str && *str ) )
706    {
707        tr_free( tor->downloadDir );
708        tor->downloadDir = tr_strdup( str );
709        fieldsLoaded |= TR_FR_DOWNLOAD_DIR;
710    }
711
712    if( ( fieldsToLoad & ( TR_FR_PROGRESS | TR_FR_INCOMPLETE_DIR ) )
713      && ( tr_bencDictFindStr( &top, KEY_INCOMPLETE_DIR, &str ) )
714      && ( str && *str ) )
715    {
716        tr_free( tor->incompleteDir );
717        tor->incompleteDir = tr_strdup( str );
718        fieldsLoaded |= TR_FR_INCOMPLETE_DIR;
719    }
720
721    if( ( fieldsToLoad & TR_FR_DOWNLOADED )
722      && tr_bencDictFindInt( &top, KEY_DOWNLOADED, &i ) )
723    {
724        tor->downloadedPrev = i;
725        fieldsLoaded |= TR_FR_DOWNLOADED;
726    }
727
728    if( ( fieldsToLoad & TR_FR_UPLOADED )
729      && tr_bencDictFindInt( &top, KEY_UPLOADED, &i ) )
730    {
731        tor->uploadedPrev = i;
732        fieldsLoaded |= TR_FR_UPLOADED;
733    }
734
735    if( ( fieldsToLoad & TR_FR_MAX_PEERS )
736      && tr_bencDictFindInt( &top, KEY_MAX_PEERS, &i ) )
737    {
738        tor->maxConnectedPeers = i;
739        fieldsLoaded |= TR_FR_MAX_PEERS;
740    }
741
742    if( ( fieldsToLoad & TR_FR_RUN )
743      && tr_bencDictFindBool( &top, KEY_PAUSED, &boolVal ) )
744    {
745        tor->isRunning = !boolVal;
746        fieldsLoaded |= TR_FR_RUN;
747    }
748
749    if( ( fieldsToLoad & TR_FR_ADDED_DATE )
750      && tr_bencDictFindInt( &top, KEY_ADDED_DATE, &i ) )
751    {
752        tor->addedDate = i;
753        fieldsLoaded |= TR_FR_ADDED_DATE;
754    }
755
756    if( ( fieldsToLoad & TR_FR_DONE_DATE )
757      && tr_bencDictFindInt( &top, KEY_DONE_DATE, &i ) )
758    {
759        tor->doneDate = i;
760        fieldsLoaded |= TR_FR_DONE_DATE;
761    }
762
763    if( ( fieldsToLoad & TR_FR_ACTIVITY_DATE )
764      && tr_bencDictFindInt( &top, KEY_ACTIVITY_DATE, &i ) )
765    {
766        tr_torrentSetActivityDate( tor, i );
767        fieldsLoaded |= TR_FR_ACTIVITY_DATE;
768    }
769
770    if( ( fieldsToLoad & TR_FR_TIME_SEEDING )
771      && tr_bencDictFindInt( &top, KEY_TIME_SEEDING, &i ) )
772    {
773        tor->secondsSeeding = i;
774        fieldsLoaded |= TR_FR_TIME_SEEDING;
775    }
776
777    if( ( fieldsToLoad & TR_FR_TIME_DOWNLOADING )
778      && tr_bencDictFindInt( &top, KEY_TIME_DOWNLOADING, &i ) )
779    {
780        tor->secondsDownloading = i;
781        fieldsLoaded |= TR_FR_TIME_DOWNLOADING;
782    }
783
784    if( ( fieldsToLoad & TR_FR_BANDWIDTH_PRIORITY )
785      && tr_bencDictFindInt( &top, KEY_BANDWIDTH_PRIORITY, &i )
786      && tr_isPriority( i ) )
787    {
788        tr_torrentSetPriority( tor, i );
789        fieldsLoaded |= TR_FR_BANDWIDTH_PRIORITY;
790    }
791
792    if( fieldsToLoad & TR_FR_PEERS )
793        fieldsLoaded |= loadPeers( &top, tor );
794
795    if( fieldsToLoad & TR_FR_FILE_PRIORITIES )
796        fieldsLoaded |= loadFilePriorities( &top, tor );
797
798    if( fieldsToLoad & TR_FR_PROGRESS )
799        fieldsLoaded |= loadProgress( &top, tor );
800
801    if( fieldsToLoad & TR_FR_DND )
802        fieldsLoaded |= loadDND( &top, tor );
803
804    if( fieldsToLoad & TR_FR_SPEEDLIMIT )
805        fieldsLoaded |= loadSpeedLimits( &top, tor );
806
807    if( fieldsToLoad & TR_FR_RATIOLIMIT )
808        fieldsLoaded |= loadRatioLimits( &top, tor );
809
810    if( fieldsToLoad & TR_FR_IDLELIMIT )
811        fieldsLoaded |= loadIdleLimits( &top, tor );
812
813    /* loading the resume file triggers of a lot of changes,
814     * but none of them needs to trigger a re-saving of the
815     * same resume information... */
816    tor->isDirty = wasDirty;
817
818    tr_bencFree( &top );
819    tr_free( filename );
820    return fieldsLoaded;
821}
822
823static uint64_t
824setFromCtor( tr_torrent *    tor,
825             uint64_t        fields,
826             const tr_ctor * ctor,
827             int             mode )
828{
829    uint64_t ret = 0;
830
831    if( fields & TR_FR_RUN )
832    {
833        bool isPaused;
834        if( !tr_ctorGetPaused( ctor, mode, &isPaused ) )
835        {
836            tor->isRunning = !isPaused;
837            ret |= TR_FR_RUN;
838        }
839    }
840
841    if( fields & TR_FR_MAX_PEERS )
842        if( !tr_ctorGetPeerLimit( ctor, mode, &tor->maxConnectedPeers ) )
843            ret |= TR_FR_MAX_PEERS;
844
845    if( fields & TR_FR_DOWNLOAD_DIR )
846    {
847        const char * path;
848        if( !tr_ctorGetDownloadDir( ctor, mode, &path ) && path && *path )
849        {
850            ret |= TR_FR_DOWNLOAD_DIR;
851            tr_free( tor->downloadDir );
852            tor->downloadDir = tr_strdup( path );
853        }
854    }
855
856    return ret;
857}
858
859static uint64_t
860useManditoryFields( tr_torrent *    tor,
861                    uint64_t        fields,
862                    const tr_ctor * ctor )
863{
864    return setFromCtor( tor, fields, ctor, TR_FORCE );
865}
866
867static uint64_t
868useFallbackFields( tr_torrent *    tor,
869                   uint64_t        fields,
870                   const tr_ctor * ctor )
871{
872    return setFromCtor( tor, fields, ctor, TR_FALLBACK );
873}
874
875uint64_t
876tr_torrentLoadResume( tr_torrent *    tor,
877                      uint64_t        fieldsToLoad,
878                      const tr_ctor * ctor )
879{
880    uint64_t ret = 0;
881
882    assert( tr_isTorrent( tor ) );
883
884    ret |= useManditoryFields( tor, fieldsToLoad, ctor );
885    fieldsToLoad &= ~ret;
886    ret |= loadFromFile( tor, fieldsToLoad );
887    fieldsToLoad &= ~ret;
888    ret |= useFallbackFields( tor, fieldsToLoad, ctor );
889
890    return ret;
891}
892
893void
894tr_torrentRemoveResume( const tr_torrent * tor )
895{
896    char * filename = getResumeFilename( tor );
897    unlink( filename );
898    tr_free( filename );
899}
900
Note: See TracBrowser for help on using the repository browser.