source: trunk/libtransmission/resume.c @ 11974

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

(trunk libT) #4047 "timeChecked for the last piece is not saved to a resume file" -- fixed.

A garden variety off-by-one error. good patch by jusid and revision by me.

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