source: trunk/libtransmission/resume.c @ 5611

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

'resume' cleanup

File size: 11.2 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:$
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_CORRUPT     "corrupt"
28#define KEY_DESTINATION "destination"
29#define KEY_DOWNLOADED  "downloaded"
30#define KEY_MAX_PEERS   "max-peers"
31#define KEY_PAUSED      "paused"
32#define KEY_PEERS       "peers"
33#define KEY_PRIORITY    "priority"
34#define KEY_PROGRESS    "progress"
35#define KEY_SPEEDLIMIT  "speed-limit"
36#define KEY_UPLOADED    "uploaded"
37
38#define KEY_SPEEDLIMIT_DOWN_SPEED "down-speed"
39#define KEY_SPEEDLIMIT_DOWN_MODE  "down-mode"
40#define KEY_SPEEDLIMIT_UP_SPEED   "up-speed"
41#define KEY_SPEEDLIMIT_UP_MODE    "up-mode"
42
43#define KEY_PROGRESS_MTIMES "mtimes"
44#define KEY_PROGRESS_BITFIELD "bitfield"
45
46static void
47getResumeFilename( char * buf, size_t buflen, const tr_torrent * tor )
48{
49    const char * dir = tr_getResumeDir( tor->handle );
50    char base[4096];
51    snprintf( base, sizeof( base ), "%s.%16.16s.resume",
52              tor->info.name,
53              tor->info.hashString );
54    tr_buildPath( buf, buflen, dir, base, NULL );
55}
56
57/***
58****
59***/
60
61static void
62savePeers( tr_benc * dict, const tr_torrent * tor )
63{
64    tr_pex * pex;
65    const int count = tr_peerMgrGetPeers( tor->handle->peerMgr,
66                                          tor->info.hash, &pex );
67    if( count > 0 ) {
68        tr_benc * child = tr_bencDictAdd( dict, KEY_PEERS );
69        tr_bencInitStrDupLen( child, (const char*)pex, sizeof(tr_pex)*count );
70        tr_free( pex );
71    }
72}
73
74static uint64_t
75loadPeers( tr_benc * dict, tr_torrent * tor )
76{
77    uint64_t ret = 0;
78    tr_benc * p;
79
80    if(( p = tr_bencDictFindType( dict, KEY_PEERS, TYPE_STR )))
81    {
82        int i;
83        const char * str = p->val.s.s;
84        const size_t len = p->val.s.i;
85        const int count = len / sizeof( tr_pex );
86        for( i=0; i<count; ++i ) {
87            tr_pex pex;
88            memcpy( &pex, str + (i*sizeof(tr_pex)), sizeof(tr_pex) );
89            tr_peerMgrAddPex( tor->handle->peerMgr,
90                              tor->info.hash, TR_PEER_FROM_CACHE, &pex );
91        }
92        tr_tordbg( tor, "Loaded %d peers from resume file", count );
93        ret = TR_FR_PEERS;
94    }
95
96    return ret;
97}
98
99/***
100****
101***/
102
103static void
104savePriorities( tr_benc * dict, const tr_torrent * tor )
105{
106    const tr_info * inf = &tor->info;
107    const tr_file_index_t n = inf->fileCount;
108    tr_file_index_t i;
109    tr_benc * list;
110
111    list = tr_bencDictAddList( dict, KEY_PRIORITY, tor->info.fileCount );
112    for( i=0; i<n; ++i )
113        tr_bencInitInt( tr_bencListAdd( list ), inf->files[i].priority );
114}
115
116static uint64_t
117loadPriorities( tr_benc * dict, tr_torrent * tor )
118{
119    uint64_t ret = 0;
120    tr_info * inf = &tor->info;
121    const tr_file_index_t n = inf->fileCount;
122    tr_benc * list;
123
124    if( tr_bencDictFindList( dict, KEY_PRIORITY, &list )
125        && ( list->val.l.count == (int)n ) )
126    {
127        int64_t tmp;
128        tr_file_index_t i;
129        for( i=0; i<n; ++i )
130            if( tr_bencGetInt( &list->val.l.vals[i], &tmp ) )
131                inf->files[i].priority = tmp;
132        ret = TR_FR_PRIORITY;
133    }
134
135    return ret;
136}
137
138/***
139****
140***/
141
142static void
143saveSpeedLimits( tr_benc * dict, const tr_torrent * tor )
144{
145    tr_benc * d = tr_bencDictAddDict( dict, KEY_SPEEDLIMIT, 4 );
146    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_DOWN_SPEED,
147                       tr_torrentGetSpeedLimit( tor, TR_DOWN ) );
148    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_DOWN_MODE,
149                       tr_torrentGetSpeedMode( tor, TR_DOWN ) );
150    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_UP_SPEED,
151                       tr_torrentGetSpeedLimit( tor, TR_UP ) );
152    tr_bencDictAddInt( d, KEY_SPEEDLIMIT_UP_MODE,
153                       tr_torrentGetSpeedMode( tor, TR_UP ) );
154}
155
156static uint64_t
157loadSpeedLimits( tr_benc * dict, tr_torrent * tor )
158{
159    uint64_t ret = 0;
160    tr_benc * d;
161
162    if( tr_bencDictFindDict( dict, KEY_SPEEDLIMIT, &d ) )
163    {
164        int64_t i;
165        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_DOWN_SPEED, &i ) )
166            tr_torrentSetSpeedLimit( tor, TR_DOWN, i );
167        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_DOWN_MODE, &i ) )
168            tr_torrentSetSpeedMode( tor, TR_DOWN, i );
169        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_UP_SPEED, &i ) )
170            tr_torrentSetSpeedLimit( tor, TR_UP, i );
171        if( tr_bencDictFindInt( d, KEY_SPEEDLIMIT_UP_MODE, &i ) )
172            tr_torrentSetSpeedMode( tor, TR_UP, i );
173        ret = TR_FR_SPEEDLIMIT;
174    }
175
176    return ret;
177}
178
179/***
180****
181***/
182
183static void
184saveProgress( tr_benc * dict, const tr_torrent * tor )
185{
186    int i;
187    int n;
188    time_t * mtimes;
189    tr_benc * p;
190    tr_benc * m;
191    tr_benc * b;
192    const tr_bitfield * bitfield;
193
194    p = tr_bencDictAdd( dict, KEY_PROGRESS );
195    tr_bencInitDict( p, 2 );
196
197    /* add the mtimes */
198    mtimes = tr_torrentGetMTimes( tor, &n );
199    m = tr_bencDictAddList( p, KEY_PROGRESS_MTIMES, n );
200    for( i=0; i<n; ++i ) {
201        if( !tr_torrentIsFileChecked( tor, i ) )
202            mtimes[i] = ~(time_t)0; /* force a recheck next time */
203        tr_bencListAddInt( m, mtimes[i] );
204    }
205
206    /* add the bitfield */
207    bitfield = tr_cpBlockBitfield( tor->completion );
208    b = tr_bencDictAdd( p, KEY_PROGRESS_BITFIELD );
209    tr_bencInitStrDupLen( b, (const char*)bitfield->bits, bitfield->len );
210
211    /* cleanup */
212    tr_free( mtimes );
213}
214
215static uint64_t
216loadProgress( tr_benc * dict, tr_torrent * tor )
217{
218    uint64_t ret = 0;
219    tr_benc * p;
220
221    if( tr_bencDictFindDict( dict, KEY_PROGRESS, &p ) )
222    {
223        tr_benc * m;
224        tr_benc * b;
225        int n;
226        time_t * curMTimes = tr_torrentGetMTimes( tor, &n );
227
228        if( tr_bencDictFindList( p, KEY_PROGRESS_MTIMES, &m )
229            && ( m->val.l.count == (int64_t)tor->info.fileCount )
230            && ( m->val.l.count == n ) )
231        {
232            int i;
233            const time_t recheck = ~(time_t)0;
234            for( i=0; i<m->val.l.count; ++i ) 
235            {
236                int64_t x;
237                time_t t = tr_bencGetInt( &m->val.l.vals[i], &x ) ? x : recheck;
238                if( ( t != recheck ) && ( curMTimes[i] == t ) )
239                    tr_torrentSetFileChecked( tor, i, TRUE );
240                else {
241                    tr_torrentSetFileChecked( tor, i, FALSE );
242                    tr_tordbg( tor, "File #%d needs to be verified", i );
243                }
244            }
245        }
246        else
247        {
248            tr_torrentUncheck( tor );
249            tr_tordbg( tor, "Torrent needs to be verified - unable to find mtimes" );
250        }
251
252        if(( b = tr_bencDictFindType( p, KEY_PROGRESS_BITFIELD, TYPE_STR )))
253        {
254            tr_bitfield tmp;
255            tmp.len = b->val.s.i;
256            tmp.bits = (uint8_t*) b->val.s.s;
257            if( tr_cpBlockBitfieldSet( tor->completion, &tmp ) ) {
258                tr_torrentUncheck( tor );
259                tr_tordbg( tor, "Torrent needs to be verified - error loading bitfield" );
260            }
261        }
262        else
263        {
264            tr_torrentUncheck( tor );
265            tr_tordbg( tor, "Torrent needs to be verified - unable to find bitfield" );
266        }
267       
268        tr_free( curMTimes );
269        ret = TR_FR_PROGRESS;
270    }
271
272    return ret;
273}
274
275/***
276****
277***/
278
279void
280tr_torrentSaveResume( const tr_torrent * tor )
281{
282    tr_benc top;
283    char filename[MAX_PATH_LENGTH];
284
285    /* populate the bencoded data */
286    tr_bencInitDict( &top, 10 );
287    tr_bencDictAddInt( &top, KEY_CORRUPT, tor->corruptPrev + tor->corruptCur );
288    tr_bencDictAddStr( &top, KEY_DESTINATION, tor->destination );
289    tr_bencDictAddInt( &top, KEY_DOWNLOADED,
290                       tor->downloadedPrev + tor->downloadedCur );
291    tr_bencDictAddInt( &top, KEY_UPLOADED,
292                             tor->uploadedPrev + tor->uploadedCur );
293    tr_bencDictAddInt( &top, KEY_MAX_PEERS, tor->maxConnectedPeers );
294    tr_bencDictAddInt( &top, KEY_PAUSED, tor->isRunning?0:1 );
295    savePeers( &top, tor );
296    savePriorities( &top, tor );
297    saveProgress( &top, tor );
298    saveSpeedLimits( &top, tor );
299
300    getResumeFilename( filename, sizeof( filename ), tor );
301    tr_bencSaveFile( filename, &top );
302
303    tr_bencFree( &top );
304}
305
306uint64_t
307tr_torrentLoadResume( tr_torrent    * tor,
308                      uint64_t        fieldsToLoad,
309                      const tr_ctor * ctor )
310{
311    int64_t i;
312    const char * str;
313    uint64_t fieldsLoaded = 0;
314    char filename[MAX_PATH_LENGTH];
315    tr_benc top;
316
317    getResumeFilename( filename, sizeof( filename ), tor );
318
319    if( tr_bencLoadFile( filename, &top ) )
320    {
321        tr_tordbg( tor, "Couldn't read \"%s\"; trying old format.", filename );
322        fieldsLoaded = tr_fastResumeLoad( tor, fieldsToLoad, ctor );
323
324        if( ( fieldsLoaded != 0 ) && ( fieldsToLoad == ~(uint64_t)0 ) )
325        {
326            tr_torrentSaveResume( tor );
327            tr_fastResumeRemove( tor );
328            tr_tordbg( tor, "Migrated resume file to \"%s\"", filename );
329        }
330
331        return fieldsLoaded;
332    }
333
334    tr_tordbg( tor, "Read resume file \"%s\"", filename );
335
336    if( ( fieldsToLoad & TR_FR_CORRUPT )
337            && tr_bencDictFindInt( &top, KEY_CORRUPT, &i ) ) {
338        tor->corruptPrev = i;
339        fieldsLoaded |= TR_FR_CORRUPT;
340    }
341
342    if( ( fieldsToLoad & TR_FR_DESTINATION )
343            && tr_bencDictFindStr( &top, KEY_DESTINATION, &str ) ) {
344        tr_free( tor->destination );
345        tor->destination = tr_strdup( str );
346        fieldsLoaded |= TR_FR_DESTINATION;
347    }
348
349    if( ( fieldsToLoad & TR_FR_DOWNLOADED )
350            && tr_bencDictFindInt( &top, KEY_DOWNLOADED, &i ) ) {
351        tor->downloadedPrev = i;
352        fieldsLoaded |= TR_FR_DOWNLOADED;
353    }
354
355    if( ( fieldsToLoad & TR_FR_UPLOADED )
356            && tr_bencDictFindInt( &top, KEY_UPLOADED, &i ) ) {
357        tor->uploadedPrev = i;
358        fieldsLoaded |= TR_FR_UPLOADED;
359    }
360
361    if( ( fieldsToLoad & TR_FR_MAX_PEERS )
362            && tr_bencDictFindInt( &top, KEY_MAX_PEERS, &i ) ) {
363        tor->maxConnectedPeers = i;
364        fieldsLoaded |= TR_FR_MAX_PEERS;
365    }
366
367    if( ( fieldsToLoad & TR_FR_RUN )
368            && tr_bencDictFindInt( &top, KEY_PAUSED, &i ) ) {
369        tor->isRunning = i ? 0 : 1;
370        fieldsLoaded |= TR_FR_RUN;
371    }
372
373    if( fieldsToLoad & TR_FR_PEERS )
374        fieldsLoaded |= loadPeers( &top, tor );
375
376    if( fieldsToLoad & TR_FR_PRIORITY )
377        fieldsLoaded |= loadPriorities( &top, tor );
378
379    if( fieldsToLoad & TR_FR_PROGRESS )
380        fieldsLoaded |= loadProgress( &top, tor );
381
382    if( fieldsToLoad & TR_FR_SPEEDLIMIT )
383        fieldsLoaded |= loadSpeedLimits( &top, tor );
384
385    tr_bencFree( &top );
386    return fieldsLoaded;
387}
388
389void
390tr_torrentRemoveResume( const tr_torrent * tor )
391{
392    char filename[MAX_PATH_LENGTH];
393    getResumeFilename( filename, sizeof( filename ), tor );
394    unlink( filename );
395    tr_fastResumeRemove( tor );
396}
Note: See TracBrowser for help on using the repository browser.