source: trunk/libtransmission/resume.c @ 7249

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

(libT) extra assertions and safeguards for #1523: crash in tr_fdFileCheckout() when starting daemon

  • Property svn:keywords set to Date Rev Author Id
File size: 16.7 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 7249 2008-12-03 01:25:45Z 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
64/* TODO: resume peers6 */
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      && ( str && *str ) )
466    {
467        tr_free( tor->downloadDir );
468        tor->downloadDir = tr_strdup( str );
469        fieldsLoaded |= TR_FR_DOWNLOAD_DIR;
470    }
471
472    if( ( fieldsToLoad & TR_FR_DOWNLOADED )
473      && tr_bencDictFindInt( &top, KEY_DOWNLOADED, &i ) )
474    {
475        tor->downloadedPrev = i;
476        fieldsLoaded |= TR_FR_DOWNLOADED;
477    }
478
479    if( ( fieldsToLoad & TR_FR_UPLOADED )
480      && tr_bencDictFindInt( &top, KEY_UPLOADED, &i ) )
481    {
482        tor->uploadedPrev = i;
483        fieldsLoaded |= TR_FR_UPLOADED;
484    }
485
486    if( ( fieldsToLoad & TR_FR_MAX_PEERS )
487      && tr_bencDictFindInt( &top, KEY_MAX_PEERS, &i ) )
488    {
489        tor->maxConnectedPeers = i;
490        fieldsLoaded |= TR_FR_MAX_PEERS;
491    }
492
493    if( ( fieldsToLoad & TR_FR_RUN )
494      && tr_bencDictFindInt( &top, KEY_PAUSED, &i ) )
495    {
496        tor->isRunning = i ? 0 : 1;
497        fieldsLoaded |= TR_FR_RUN;
498    }
499
500    if( ( fieldsToLoad & TR_FR_ADDED_DATE )
501      && tr_bencDictFindInt( &top, KEY_ADDED_DATE, &i ) )
502    {
503        tor->addedDate = i;
504        fieldsLoaded |= TR_FR_ADDED_DATE;
505    }
506
507    if( ( fieldsToLoad & TR_FR_DONE_DATE )
508      && tr_bencDictFindInt( &top, KEY_DONE_DATE, &i ) )
509    {
510        tor->doneDate = i;
511        fieldsLoaded |= TR_FR_DONE_DATE;
512    }
513
514    if( ( fieldsToLoad & TR_FR_ACTIVITY_DATE )
515      && tr_bencDictFindInt( &top, KEY_ACTIVITY_DATE, &i ) )
516    {
517        tor->activityDate = i;
518        fieldsLoaded |= TR_FR_ACTIVITY_DATE;
519    }
520
521    if( fieldsToLoad & TR_FR_PEERS )
522        fieldsLoaded |= loadPeers( &top, tor );
523
524    if( fieldsToLoad & TR_FR_PRIORITY )
525        fieldsLoaded |= loadPriorities( &top, tor );
526
527    if( fieldsToLoad & TR_FR_PROGRESS )
528        fieldsLoaded |= loadProgress( &top, tor );
529
530    if( fieldsToLoad & TR_FR_DND )
531        fieldsLoaded |= loadDND( &top, tor );
532
533    if( fieldsToLoad & TR_FR_SPEEDLIMIT )
534        fieldsLoaded |= loadSpeedLimits( &top, tor );
535
536    tr_bencFree( &top );
537    tr_free( filename );
538    return fieldsLoaded;
539}
540
541static uint64_t
542setFromCtor( tr_torrent *    tor,
543             uint64_t        fields,
544             const tr_ctor * ctor,
545             int             mode )
546{
547    uint64_t ret = 0;
548
549    if( fields & TR_FR_RUN )
550    {
551        uint8_t isPaused;
552        if( !tr_ctorGetPaused( ctor, mode, &isPaused ) )
553        {
554            tor->isRunning = !isPaused;
555            ret |= TR_FR_RUN;
556        }
557    }
558
559    if( fields & TR_FR_MAX_PEERS )
560        if( !tr_ctorGetPeerLimit( ctor, mode, &tor->maxConnectedPeers ) )
561            ret |= TR_FR_MAX_PEERS;
562
563    if( fields & TR_FR_DOWNLOAD_DIR )
564    {
565        const char * path;
566        if( !tr_ctorGetDownloadDir( ctor, mode, &path ) && path && *path )
567        {
568            ret |= TR_FR_DOWNLOAD_DIR;
569            tr_free( tor->downloadDir );
570            tor->downloadDir = tr_strdup( path );
571        }
572    }
573
574    return ret;
575}
576
577static uint64_t
578useManditoryFields( tr_torrent *    tor,
579                    uint64_t        fields,
580                    const tr_ctor * ctor )
581{
582    return setFromCtor( tor, fields, ctor, TR_FORCE );
583}
584
585static uint64_t
586useFallbackFields( tr_torrent *    tor,
587                   uint64_t        fields,
588                   const tr_ctor * ctor )
589{
590    return setFromCtor( tor, fields, ctor, TR_FALLBACK );
591}
592
593uint64_t
594tr_torrentLoadResume( tr_torrent *    tor,
595                      uint64_t        fieldsToLoad,
596                      const tr_ctor * ctor )
597{
598    uint64_t ret = 0;
599
600    ret |= useManditoryFields( tor, fieldsToLoad, ctor );
601    fieldsToLoad &= ~ret;
602    ret |= loadFromFile( tor, fieldsToLoad );
603    fieldsToLoad &= ~ret;
604    ret |= useFallbackFields( tor, fieldsToLoad, ctor );
605
606    return ret;
607}
608
609void
610tr_torrentRemoveResume( const tr_torrent * tor )
611{
612    char * filename = getResumeFilename( tor );
613
614    unlink( filename );
615    tr_fastResumeRemove( tor );
616    tr_free( filename );
617}
618
Note: See TracBrowser for help on using the repository browser.