source: trunk/libtransmission/resume.c @ 7476

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

(trunk libT) #include "session.h" cleanup from wereHamster

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