source: branches/1.4x/libtransmission/resume.c @ 7455

Last change on this file since 7455 was 7455, checked in by charles, 12 years ago

(1.4x libT) backport handshake, peer, bandwidth, peer-io to 1.4x.

  • Property svn:keywords set to Date Rev Author Id
File size: 16.6 KB
Line 
1/*
2 * This file Copyright (C) 2008 Charles Kerr <charles@rebelbase.com>
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 7455 2008-12-22 00:51:14Z charles $
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 "fastresume.h"
21#include "net.h"
22#include "peer-mgr.h" /* pex */
23#include "platform.h" /* tr_getResumeDir */
24#include "resume.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_MAX_PEERS       "max-peers"
36#define KEY_PAUSED          "paused"
37#define KEY_PEERS           "peers"
38#define KEY_PRIORITY        "priority"
39#define KEY_PROGRESS        "progress"
40#define KEY_SPEEDLIMIT      "speed-limit"
41#define KEY_UPLOADED        "uploaded"
42
43#define KEY_SPEEDLIMIT_DOWN_SPEED "down-speed"
44#define KEY_SPEEDLIMIT_DOWN_MODE  "down-mode"
45#define KEY_SPEEDLIMIT_UP_SPEED   "up-speed"
46#define KEY_SPEEDLIMIT_UP_MODE    "up-mode"
47
48#define KEY_PROGRESS_MTIMES   "mtimes"
49#define KEY_PROGRESS_BITFIELD "bitfield"
50
51static char*
52getResumeFilename( const tr_torrent * tor )
53{
54    return tr_strdup_printf( "%s%c%s.%16.16s.resume",
55                             tr_getResumeDir( tor->session ),
56                             TR_PATH_DELIMITER,
57                             tor->info.name,
58                             tor->info.hashString );
59}
60
61/***
62****
63***/
64
65static void
66savePeers( tr_benc *          dict,
67           const tr_torrent * tor )
68{
69    tr_pex *  pex = NULL;
70    const int count = tr_peerMgrGetPeers( tor->session->peerMgr,
71                                          tor->info.hash, &pex );
72
73    if( count > 0 )
74        tr_bencDictAddRaw( dict, KEY_PEERS, pex, sizeof( tr_pex ) * count );
75    tr_free( pex );
76}
77
78static uint64_t
79loadPeers( tr_benc *    dict,
80           tr_torrent * tor )
81{
82    uint64_t        ret = 0;
83    const uint8_t * str;
84    size_t          len;
85
86    if( tr_bencDictFindRaw( dict, KEY_PEERS, &str, &len ) )
87    {
88        int       i;
89        const int count = len / sizeof( tr_pex );
90        for( i = 0; i < count; ++i )
91        {
92            tr_pex pex;
93            memcpy( &pex, str + ( i * sizeof( tr_pex ) ), sizeof( tr_pex ) );
94            tr_peerMgrAddPex( tor->session->peerMgr,
95                              tor->info.hash, TR_PEER_FROM_CACHE, &pex );
96        }
97        tr_tordbg( tor, "Loaded %d peers from resume file", count );
98        ret = TR_FR_PEERS;
99    }
100
101    return ret;
102}
103
104/***
105****
106***/
107
108static void
109saveDND( tr_benc *          dict,
110         const tr_torrent * tor )
111{
112    const tr_info *       inf = tr_torrentInfo( tor );
113    const tr_file_index_t n = inf->fileCount;
114    tr_file_index_t       i;
115    tr_benc *             list;
116
117    list = tr_bencDictAddList( dict, KEY_DND, n );
118    for( i = 0; i < n; ++i )
119        tr_bencListAddInt( list, inf->files[i].dnd ? 1 : 0 );
120}
121
122static uint64_t
123loadDND( tr_benc *    dict,
124         tr_torrent * tor )
125{
126    uint64_t              ret = 0;
127    tr_info *             inf = &tor->info;
128    const tr_file_index_t n = inf->fileCount;
129    tr_benc *             list = NULL;
130
131    if( tr_bencDictFindList( dict, KEY_DND, &list )
132      && ( tr_bencListSize( list ) == n ) )
133    {
134        int64_t           tmp;
135        tr_file_index_t * dl = tr_new( tr_file_index_t, n );
136        tr_file_index_t * dnd = tr_new( tr_file_index_t, n );
137        tr_file_index_t   i, dlCount = 0, dndCount = 0;
138
139        for( i = 0; i < n; ++i )
140        {
141            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) && tmp )
142                dnd[dndCount++] = i;
143            else
144                dl[dlCount++] = i;
145        }
146
147        if( dndCount )
148        {
149            tr_torrentInitFileDLs ( tor, dnd, dndCount, FALSE );
150            tr_tordbg( tor, "Resume file found %d files listed as dnd",
151                       dndCount );
152        }
153        if( dlCount )
154        {
155            tr_torrentInitFileDLs ( tor, dl, dlCount, TRUE );
156            tr_tordbg( tor,
157                       "Resume file found %d files marked for download",
158                       dlCount );
159        }
160
161        tr_free( dnd );
162        tr_free( dl );
163        ret = TR_FR_PRIORITY;
164    }
165    else
166    {
167        tr_tordbg(
168            tor,
169            "Couldn't load DND flags.  dnd list (%p) has %zu children; torrent has %d files",
170            list, tr_bencListSize( list ), (int)n );
171    }
172
173    return ret;
174}
175
176/***
177****
178***/
179
180static void
181savePriorities( tr_benc *          dict,
182                const tr_torrent * tor )
183{
184    const tr_info *       inf = tr_torrentInfo( tor );
185    const tr_file_index_t n = inf->fileCount;
186    tr_file_index_t       i;
187    tr_benc *             list;
188
189    list = tr_bencDictAddList( dict, KEY_PRIORITY, n );
190    for( i = 0; i < n; ++i )
191        tr_bencListAddInt( list, inf->files[i].priority );
192}
193
194static uint64_t
195loadPriorities( tr_benc *    dict,
196                tr_torrent * tor )
197{
198    uint64_t              ret = 0;
199    tr_info *             inf = &tor->info;
200    const tr_file_index_t n = inf->fileCount;
201    tr_benc *             list;
202
203    if( tr_bencDictFindList( dict, KEY_PRIORITY, &list )
204      && ( tr_bencListSize( list ) == n ) )
205    {
206        int64_t         tmp;
207        tr_file_index_t i;
208        for( i = 0; i < n; ++i )
209            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) )
210                inf->files[i].priority = tmp;
211        ret = TR_FR_PRIORITY;
212    }
213
214    return ret;
215}
216
217/***
218****
219***/
220
221static void
222saveSpeedLimits( tr_benc *          dict,
223                 const tr_torrent * tor )
224{
225    tr_benc * d = tr_bencDictAddDict( dict, KEY_SPEEDLIMIT, 4 );
226
227    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_DOWN_SPEED,
228                      tr_torrentGetSpeedLimit( tor, TR_DOWN ) );
229    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_DOWN_MODE,
230                      tr_torrentGetSpeedMode( tor, TR_DOWN ) );
231    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_UP_SPEED,
232                      tr_torrentGetSpeedLimit( tor, TR_UP ) );
233    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_UP_MODE,
234                      tr_torrentGetSpeedMode( tor, TR_UP ) );
235}
236
237static uint64_t
238loadSpeedLimits( tr_benc *    dict,
239                 tr_torrent * tor )
240{
241    uint64_t  ret = 0;
242    tr_benc * d;
243
244    if( tr_bencDictFindDict( dict, KEY_SPEEDLIMIT, &d ) )
245    {
246        int64_t i;
247        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_DOWN_SPEED, &i ) )
248            tr_torrentSetSpeedLimit( tor, TR_DOWN, i );
249        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_DOWN_MODE, &i ) )
250            tr_torrentSetSpeedMode( tor, TR_DOWN, i );
251        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_UP_SPEED, &i ) )
252            tr_torrentSetSpeedLimit( tor, TR_UP, i );
253        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_UP_MODE, &i ) )
254            tr_torrentSetSpeedMode( tor, TR_UP, i );
255        ret = TR_FR_SPEEDLIMIT;
256    }
257
258    return ret;
259}
260
261/***
262****
263***/
264
265static void
266saveProgress( tr_benc *          dict,
267              const tr_torrent * tor )
268{
269    size_t              i, n;
270    time_t *            mtimes;
271    tr_benc *           p;
272    tr_benc *           m;
273    const tr_bitfield * bitfield;
274
275    p = tr_bencDictAdd( dict, KEY_PROGRESS );
276    tr_bencInitDict( p, 2 );
277
278    /* add the mtimes */
279    mtimes = tr_torrentGetMTimes( tor, &n );
280    m = tr_bencDictAddList( p, KEY_PROGRESS_MTIMES, n );
281    for( i = 0; i < n; ++i )
282    {
283        if( !tr_torrentIsFileChecked( tor, i ) )
284            mtimes[i] = ~(time_t)0; /* force a recheck */
285        tr_bencListAddInt( m, mtimes[i] );
286    }
287
288    /* add the bitfield */
289    bitfield = tr_cpBlockBitfield( tor->completion );
290    tr_bencDictAddRaw( p, KEY_PROGRESS_BITFIELD,
291                       bitfield->bits, bitfield->byteCount );
292
293    /* cleanup */
294    tr_free( mtimes );
295}
296
297static uint64_t
298loadProgress( tr_benc *    dict,
299              tr_torrent * tor )
300{
301    uint64_t  ret = 0;
302    tr_benc * p;
303
304    if( tr_bencDictFindDict( dict, KEY_PROGRESS, &p ) )
305    {
306        const uint8_t * raw;
307        size_t          rawlen;
308        tr_benc *       m;
309        size_t          n;
310        time_t *        curMTimes = tr_torrentGetMTimes( tor, &n );
311
312        if( tr_bencDictFindList( p, KEY_PROGRESS_MTIMES, &m )
313          && ( n == tor->info.fileCount )
314          && ( n == tr_bencListSize( m ) ) )
315        {
316            size_t i;
317            for( i = 0; i < n; ++i )
318            {
319                int64_t tmp;
320                if( !tr_bencGetInt( tr_bencListChild( m, i ), &tmp ) )
321                {
322                    tr_tordbg(
323                        tor,
324                        "File #%zu needs to be verified - couldn't find benc entry",
325                        i );
326                    tr_torrentSetFileChecked( tor, i, FALSE );
327                }
328                else
329                {
330                    const time_t t = (time_t) tmp;
331                    if( t == curMTimes[i] )
332                        tr_torrentSetFileChecked( tor, i, TRUE );
333                    else
334                    {
335                        tr_tordbg(
336                            tor,
337                            "File #%zu needs to be verified - times %lu and %lu don't match",
338                            i, t, curMTimes[i] );
339                        tr_torrentSetFileChecked( tor, i, FALSE );
340                    }
341                }
342            }
343        }
344        else
345        {
346            tr_torrentUncheck( tor );
347            tr_tordbg(
348                tor, "Torrent needs to be verified - unable to find mtimes" );
349        }
350
351        if( tr_bencDictFindRaw( p, KEY_PROGRESS_BITFIELD, &raw, &rawlen ) )
352        {
353            tr_bitfield tmp;
354            tmp.byteCount = rawlen;
355            tmp.bitCount = tmp.byteCount * 8;
356            tmp.bits = (uint8_t*) raw;
357            if( !tr_cpBlockBitfieldSet( tor->completion, &tmp ) )
358            {
359                tr_torrentUncheck( tor );
360                tr_tordbg(
361                    tor,
362                    "Torrent needs to be verified - error loading bitfield" );
363            }
364        }
365        else
366        {
367            tr_torrentUncheck( tor );
368            tr_tordbg(
369                tor,
370                "Torrent needs to be verified - unable to find bitfield" );
371        }
372
373        tr_free( curMTimes );
374        ret = TR_FR_PROGRESS;
375    }
376
377    return ret;
378}
379
380/***
381****
382***/
383
384void
385tr_torrentSaveResume( const tr_torrent * tor )
386{
387    tr_benc top;
388    char *  filename;
389
390    if( !tor )
391        return;
392
393    tr_bencInitDict( &top, 14 );
394    tr_bencDictAddInt( &top, KEY_ACTIVITY_DATE,
395                       tor->activityDate );
396    tr_bencDictAddInt( &top, KEY_ADDED_DATE,
397                       tor->addedDate );
398    tr_bencDictAddInt( &top, KEY_CORRUPT,
399                       tor->corruptPrev + tor->corruptCur );
400    tr_bencDictAddInt( &top, KEY_DONE_DATE,
401                       tor->doneDate );
402    tr_bencDictAddStr( &top, KEY_DOWNLOAD_DIR,
403                       tor->downloadDir );
404    tr_bencDictAddInt( &top, KEY_DOWNLOADED,
405                       tor->downloadedPrev + tor->downloadedCur );
406    tr_bencDictAddInt( &top, KEY_UPLOADED,
407                       tor->uploadedPrev + tor->uploadedCur );
408    tr_bencDictAddInt( &top, KEY_MAX_PEERS,
409                       tor->maxConnectedPeers );
410    tr_bencDictAddInt( &top, KEY_PAUSED,
411                       tor->isRunning ? 0 : 1 );
412    savePeers( &top, tor );
413    savePriorities( &top, tor );
414    saveDND( &top, tor );
415    saveProgress( &top, tor );
416    saveSpeedLimits( &top, tor );
417
418    filename = getResumeFilename( tor );
419    tr_bencSaveFile( filename, &top );
420    tr_free( filename );
421
422    tr_bencFree( &top );
423}
424
425static uint64_t
426loadFromFile( tr_torrent * tor,
427              uint64_t     fieldsToLoad )
428{
429    int64_t      i;
430    const char * str;
431    uint64_t     fieldsLoaded = 0;
432    char *       filename;
433    tr_benc      top;
434
435    filename = getResumeFilename( tor );
436
437    if( tr_bencLoadFile( filename, &top ) )
438    {
439        tr_tordbg( tor, "Couldn't read \"%s\"; trying old format.",
440                   filename );
441        fieldsLoaded = tr_fastResumeLoad( tor, fieldsToLoad );
442
443        if( ( fieldsLoaded != 0 ) && ( fieldsToLoad == ~(uint64_t)0 ) )
444        {
445            tr_torrentSaveResume( tor );
446            tr_fastResumeRemove( tor );
447            tr_tordbg( tor, "Migrated resume file to \"%s\"", filename );
448        }
449
450        tr_free( filename );
451        return fieldsLoaded;
452    }
453
454    tr_tordbg( tor, "Read resume file \"%s\"", filename );
455
456    if( ( fieldsToLoad & TR_FR_CORRUPT )
457      && tr_bencDictFindInt( &top, KEY_CORRUPT, &i ) )
458    {
459        tor->corruptPrev = i;
460        fieldsLoaded |= TR_FR_CORRUPT;
461    }
462
463    if( ( fieldsToLoad & ( TR_FR_PROGRESS | TR_FR_DOWNLOAD_DIR ) )
464      && tr_bencDictFindStr( &top, KEY_DOWNLOAD_DIR, &str ) )
465    {
466        tr_free( tor->downloadDir );
467        tor->downloadDir = tr_strdup( str );
468        fieldsLoaded |= TR_FR_DOWNLOAD_DIR;
469    }
470
471    if( ( fieldsToLoad & TR_FR_DOWNLOADED )
472      && tr_bencDictFindInt( &top, KEY_DOWNLOADED, &i ) )
473    {
474        tor->downloadedPrev = i;
475        fieldsLoaded |= TR_FR_DOWNLOADED;
476    }
477
478    if( ( fieldsToLoad & TR_FR_UPLOADED )
479      && tr_bencDictFindInt( &top, KEY_UPLOADED, &i ) )
480    {
481        tor->uploadedPrev = i;
482        fieldsLoaded |= TR_FR_UPLOADED;
483    }
484
485    if( ( fieldsToLoad & TR_FR_MAX_PEERS )
486      && tr_bencDictFindInt( &top, KEY_MAX_PEERS, &i ) )
487    {
488        tor->maxConnectedPeers = i;
489        fieldsLoaded |= TR_FR_MAX_PEERS;
490    }
491
492    if( ( fieldsToLoad & TR_FR_RUN )
493      && tr_bencDictFindInt( &top, KEY_PAUSED, &i ) )
494    {
495        tor->isRunning = i ? 0 : 1;
496        fieldsLoaded |= TR_FR_RUN;
497    }
498
499    if( ( fieldsToLoad & TR_FR_ADDED_DATE )
500      && tr_bencDictFindInt( &top, KEY_ADDED_DATE, &i ) )
501    {
502        tor->addedDate = i;
503        fieldsLoaded |= TR_FR_ADDED_DATE;
504    }
505
506    if( ( fieldsToLoad & TR_FR_DONE_DATE )
507      && tr_bencDictFindInt( &top, KEY_DONE_DATE, &i ) )
508    {
509        tor->doneDate = i;
510        fieldsLoaded |= TR_FR_DONE_DATE;
511    }
512
513    if( ( fieldsToLoad & TR_FR_ACTIVITY_DATE )
514      && tr_bencDictFindInt( &top, KEY_ACTIVITY_DATE, &i ) )
515    {
516        tor->activityDate = i;
517        fieldsLoaded |= TR_FR_ACTIVITY_DATE;
518    }
519
520    if( fieldsToLoad & TR_FR_PEERS )
521        fieldsLoaded |= loadPeers( &top, tor );
522
523    if( fieldsToLoad & TR_FR_PRIORITY )
524        fieldsLoaded |= loadPriorities( &top, tor );
525
526    if( fieldsToLoad & TR_FR_PROGRESS )
527        fieldsLoaded |= loadProgress( &top, tor );
528
529    if( fieldsToLoad & TR_FR_DND )
530        fieldsLoaded |= loadDND( &top, tor );
531
532    if( fieldsToLoad & TR_FR_SPEEDLIMIT )
533        fieldsLoaded |= loadSpeedLimits( &top, tor );
534
535    tr_bencFree( &top );
536    tr_free( filename );
537    return fieldsLoaded;
538}
539
540static uint64_t
541setFromCtor( tr_torrent *    tor,
542             uint64_t        fields,
543             const tr_ctor * ctor,
544             int             mode )
545{
546    uint64_t ret = 0;
547
548    if( fields & TR_FR_RUN )
549    {
550        uint8_t isPaused;
551        if( !tr_ctorGetPaused( ctor, mode, &isPaused ) )
552        {
553            tor->isRunning = !isPaused;
554            ret |= TR_FR_RUN;
555        }
556    }
557
558    if( fields & TR_FR_MAX_PEERS )
559        if( !tr_ctorGetPeerLimit( ctor, mode, &tor->maxConnectedPeers ) )
560            ret |= TR_FR_MAX_PEERS;
561
562    if( fields & TR_FR_DOWNLOAD_DIR )
563    {
564        const char * downloadDir;
565        if( !tr_ctorGetDownloadDir( ctor, mode, &downloadDir ) )
566        {
567            ret |= TR_FR_DOWNLOAD_DIR;
568            tr_free( tor->downloadDir );
569            tor->downloadDir = tr_strdup( downloadDir );
570        }
571    }
572
573    return ret;
574}
575
576static uint64_t
577useManditoryFields( tr_torrent *    tor,
578                    uint64_t        fields,
579                    const tr_ctor * ctor )
580{
581    return setFromCtor( tor, fields, ctor, TR_FORCE );
582}
583
584static uint64_t
585useFallbackFields( tr_torrent *    tor,
586                   uint64_t        fields,
587                   const tr_ctor * ctor )
588{
589    return setFromCtor( tor, fields, ctor, TR_FALLBACK );
590}
591
592uint64_t
593tr_torrentLoadResume( tr_torrent *    tor,
594                      uint64_t        fieldsToLoad,
595                      const tr_ctor * ctor )
596{
597    uint64_t ret = 0;
598
599    ret |= useManditoryFields( tor, fieldsToLoad, ctor );
600    fieldsToLoad &= ~ret;
601    ret |= loadFromFile( tor, fieldsToLoad );
602    fieldsToLoad &= ~ret;
603    ret |= useFallbackFields( tor, fieldsToLoad, ctor );
604
605    return ret;
606}
607
608void
609tr_torrentRemoveResume( const tr_torrent * tor )
610{
611    char * filename = getResumeFilename( tor );
612
613    unlink( filename );
614    tr_fastResumeRemove( tor );
615    tr_free( filename );
616}
617
Note: See TracBrowser for help on using the repository browser.