source: trunk/libtransmission/resume.c @ 12918

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

(trunk libT) copyediting in resume.c

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