source: trunk/libtransmission/resume.c @ 7888

Last change on this file since 7888 was 7888, checked in by charles, 13 years ago

(trunk) #1787: add support for seeding ratio limiting in libtransmission

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