source: trunk/libtransmission/resume.c @ 12204

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

(trunk) #4138 "use stdbool.h instead of tr_bool" -- done.

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