source: trunk/libtransmission/resume.c @ 6840

Last change on this file since 6840 was 6840, checked in by charles, 14 years ago

(libT) eliminating tr_errno, part 3: don't use it in tr_cpBlockBitfieldSet()

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