source: trunk/libtransmission/fastresume.c @ 7829

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

(trunk libT) add notation explaining the status between resume.c and fastresume.c

  • Property svn:keywords set to Date Rev Author Id
File size: 16.3 KB
Line 
1/******************************************************************************
2 * $Id: fastresume.c 7829 2009-02-05 16:02:38Z charles $
3 *
4 * Copyright (c) 2005-2008 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25/*
26 * NOTE: THIS FILE IS DEPRECATED
27 *
28 *  The fastresume file format is brittle and was replaced in Transmission 1.20
29 *  with the benc-formatted ".resume" files implemented in resume.[ch].
30 *
31 *  This older format is kept only for reading older resume files for users
32 *  who upgrade from older versions of Transmission, and may be removed
33 *  after more time has passed.
34 */ 
35
36/***********************************************************************
37 * Fast resume
38 ***********************************************************************
39 * The format of the resume file is a 4 byte format version (currently 1),
40 * followed by several variable-sized blocks of data.  Each block is
41 * preceded by a 1 byte ID and a 4 byte length.  The currently recognized
42 * IDs are defined below by the FR_ID_* macros.  The length does not include
43 * the 5 bytes for the ID and length.
44 *
45 * The name of the resume file is "resume.<hash>-<tag>", although
46 * older files with a name of "resume.<hash>" will be recognized if
47 * the former doesn't exist.
48 *
49 * All values are stored in the native endianness. Moving a
50 * libtransmission resume file from an architecture to another will not
51 * work, although it will not hurt either (the version will be wrong,
52 * so the resume file will not be read).
53 **********************************************************************/
54
55#include <assert.h>
56#include <errno.h>
57#include <stdio.h>
58#include <stdlib.h> /* calloc */
59#include <string.h> /* strcpy, memset, memcmp */
60
61#include <sys/types.h>
62#include <sys/stat.h> /* stat */
63#include <unistd.h> /* unlink */
64
65#include "transmission.h"
66#include "session.h"
67#include "completion.h"
68#include "fastresume.h"
69#include "peer-mgr.h"
70#include "platform.h"
71#include "resume.h" /* TR_FR_ bitwise enum */
72#include "torrent.h"
73#include "utils.h"
74
75/* time_t can be 32 or 64 bits... for consistency we'll hardwire 64 */
76typedef uint64_t tr_time_t;
77
78enum
79{
80    /* number of bytes downloaded */
81    FR_ID_DOWNLOADED = 2,
82
83    /* number of bytes uploaded */
84    FR_ID_UPLOADED = 3,
85
86    /* progress data:
87     *  - 4 bytes * number of files: mtimes of files
88     *  - 1 bit * number of blocks: whether we have the block or not */
89    FR_ID_PROGRESS = 5,
90
91    /* dnd and priority
92     * char * number of files: l,n,h for low, normal, high priority
93     * char * number of files: t,f for DND flags */
94    FR_ID_PRIORITY = 6,
95
96    /* transfer speeds
97     * uint32_t: dl speed rate to use when the mode is single
98     * uint32_t: dl's tr_speedlimit
99     * uint32_t: ul speed rate to use when the mode is single
100     * uint32_t: ul's tr_speedlimit
101     */
102    FR_ID_SPEED = 8,
103
104    /* active
105     * char: 't' if running, 'f' if paused
106     */
107    FR_ID_RUN = 9,
108
109    /* number of corrupt bytes downloaded */
110    FR_ID_CORRUPT = 10,
111
112    /* IPs and ports of connectable peers */
113    FR_ID_PEERS = 11,
114
115    /* download dir of the torrent: zero-terminated string */
116    FR_ID_DOWNLOAD_DIR = 12,
117
118    /* pex flag
119     * 't' if pex is enabled, 'f' if disabled */
120    FR_ID_PEX = 13,
121
122    /* max connected peers -- uint16_t */
123    FR_ID_MAX_PEERS = 14
124};
125
126
127/* macros for the length of various pieces of the progress data */
128#define FR_MTIME_LEN( t ) \
129    ( sizeof( tr_time_t ) * ( t )->info.fileCount )
130#define FR_BLOCK_BITFIELD_LEN( t ) \
131    ( ( ( t )->blockCount + 7u ) / 8u )
132#define FR_PROGRESS_LEN( t ) \
133    ( FR_MTIME_LEN( t ) + FR_BLOCK_BITFIELD_LEN( t ) )
134#define FR_SPEED_LEN ( 2 * ( sizeof( uint16_t ) + sizeof( uint8_t ) ) )
135
136static tr_time_t*
137getMTimes( const tr_torrent * tor,
138           int *              setme_n )
139{
140    int         i;
141    const int   n = tor->info.fileCount;
142    tr_time_t * m = calloc( n, sizeof( tr_time_t ) );
143
144    for( i = 0; i < n; ++i )
145    {
146        struct stat sb;
147        char * fname = tr_buildPath( tor->downloadDir, tor->info.files[i].name, NULL );
148        if( !stat( fname, &sb ) && S_ISREG( sb.st_mode ) )
149        {
150#ifdef SYS_DARWIN
151            m[i] = sb.st_mtimespec.tv_sec;
152#else
153            m[i] = sb.st_mtime;
154#endif
155        }
156        tr_free( fname );
157    }
158
159    *setme_n = n;
160    return m;
161}
162
163/***
164****
165***/
166
167static uint64_t
168internalIdToPublicBitfield( uint8_t id )
169{
170    uint64_t ret = 0;
171
172    switch( id )
173    {
174        case FR_ID_DOWNLOADED:
175            ret = TR_FR_DOWNLOADED;    break;
176
177        case FR_ID_UPLOADED:
178            ret = TR_FR_UPLOADED;      break;
179
180        case FR_ID_PROGRESS:
181            ret = TR_FR_PROGRESS;      break;
182
183        case FR_ID_PRIORITY:
184            ret = TR_FR_PRIORITY;      break;
185
186        case FR_ID_SPEED:
187            ret = TR_FR_SPEEDLIMIT;    break;
188
189        case FR_ID_RUN:
190            ret = TR_FR_RUN;           break;
191
192        case FR_ID_CORRUPT:
193            ret = TR_FR_CORRUPT;       break;
194
195        case FR_ID_PEERS:
196            ret = TR_FR_PEERS;         break;
197
198        case FR_ID_DOWNLOAD_DIR:
199            ret = TR_FR_DOWNLOAD_DIR;  break;
200
201        case FR_ID_MAX_PEERS:
202            ret = TR_FR_MAX_PEERS;     break;
203    }
204
205    return ret;
206}
207
208static void
209readBytes( void *           target,
210           const uint8_t ** source,
211           size_t           byteCount )
212{
213    memcpy( target, *source, byteCount );
214    *source += byteCount;
215}
216
217static uint64_t
218parseDownloaded( tr_torrent *    tor,
219                 const uint8_t * buf,
220                 uint32_t        len )
221{
222    if( len != sizeof( uint64_t ) )
223        return 0;
224    readBytes( &tor->downloadedPrev, &buf, sizeof( uint64_t ) );
225    return TR_FR_DOWNLOADED;
226}
227
228static uint64_t
229parseUploaded( tr_torrent *    tor,
230               const uint8_t * buf,
231               uint32_t        len )
232{
233    if( len != sizeof( uint64_t ) )
234        return 0;
235    readBytes( &tor->uploadedPrev, &buf, sizeof( uint64_t ) );
236    return TR_FR_UPLOADED;
237}
238
239static uint64_t
240parseCorrupt( tr_torrent *    tor,
241              const uint8_t * buf,
242              uint32_t        len )
243{
244    if( len != sizeof( uint64_t ) )
245        return 0;
246    readBytes( &tor->corruptPrev, &buf, sizeof( uint64_t ) );
247    return TR_FR_CORRUPT;
248}
249
250static uint64_t
251parseConnections( tr_torrent *    tor,
252                  const uint8_t * buf,
253                  uint32_t        len )
254{
255    if( len != sizeof( uint16_t ) )
256        return 0;
257    readBytes( &tor->maxConnectedPeers, &buf, sizeof( uint16_t ) );
258    return TR_FR_MAX_PEERS;
259}
260
261static uint64_t
262parseProgress( tr_torrent *    tor,
263               const uint8_t * buf,
264               uint32_t        len )
265{
266    uint64_t ret = 0;
267
268    if( len == FR_PROGRESS_LEN( tor ) )
269    {
270        int             i;
271        int             n;
272        tr_bitfield     bitfield;
273
274        /* compare file mtimes */
275        tr_time_t *     curMTimes = getMTimes( tor, &n );
276        const uint8_t * walk = buf;
277        tr_time_t       mtime;
278        for( i = 0; i < n; ++i )
279        {
280            readBytes( &mtime, &walk, sizeof( tr_time_t ) );
281            if( curMTimes[i] == mtime )
282                tr_torrentSetFileChecked( tor, i, TRUE );
283            else
284            {
285                tr_torrentSetFileChecked( tor, i, FALSE );
286                tr_tordbg( tor, "Torrent needs to be verified" );
287            }
288        }
289        free( curMTimes );
290
291        /* get the completion bitfield */
292        memset( &bitfield, 0, sizeof bitfield );
293        bitfield.byteCount = FR_BLOCK_BITFIELD_LEN( tor );
294        bitfield.bitCount = bitfield.byteCount * 8;
295        bitfield.bits = (uint8_t*) walk;
296        if( tr_cpBlockBitfieldSet( &tor->completion, &bitfield ) )
297            ret = TR_FR_PROGRESS;
298        else {
299            tr_torrentUncheck( tor );
300            tr_tordbg( tor, "Torrent needs to be verified" );
301        }
302    }
303
304    /* the files whose mtimes are wrong,
305       remove from completion pending a recheck... */
306    {
307        tr_piece_index_t i;
308        for( i = 0; i < tor->info.pieceCount; ++i )
309            if( !tr_torrentIsPieceChecked( tor, i ) )
310                tr_cpPieceRem( &tor->completion, i );
311    }
312
313    return ret;
314}
315
316static uint64_t
317parsePriorities( tr_torrent *    tor,
318                 const uint8_t * buf,
319                 uint32_t        len )
320{
321    uint64_t ret = 0;
322
323    if( len == (uint32_t)( 2 * tor->info.fileCount ) )
324    {
325        const size_t     n = tor->info.fileCount;
326        const uint8_t *  walk = buf;
327        tr_file_index_t *dnd = NULL, dndCount = 0;
328        tr_file_index_t *dl = NULL, dlCount = 0;
329        size_t           i;
330
331        len = 2 * n;
332
333        /* set file priorities */
334        for( i = 0; i < n; ++i )
335        {
336            tr_priority_t priority;
337            const char    ch = *walk++;
338            switch( ch )
339            {
340                case 'l':
341                    priority = TR_PRI_LOW; break;
342
343                case 'h':
344                    priority = TR_PRI_HIGH; break;
345
346                default:
347                    priority = TR_PRI_NORMAL; break;
348            }
349            tr_torrentInitFilePriority( tor, i, priority );
350        }
351
352        /* set the dnd flags */
353        dl = tr_new( tr_file_index_t, len );
354        dnd = tr_new( tr_file_index_t, len );
355        for( i = 0; i < n; ++i )
356            if( *walk++ == 't' ) /* 't' means the DND flag is true */
357                dnd[dndCount++] = i;
358            else
359                dl[dlCount++] = i;
360
361        if( dndCount )
362            tr_torrentInitFileDLs ( tor, dnd, dndCount, FALSE );
363        if( dlCount )
364            tr_torrentInitFileDLs ( tor, dl, dlCount, TRUE );
365
366        tr_free( dnd );
367        tr_free( dl );
368
369        ret = TR_FR_PRIORITY;
370    }
371
372    return ret;
373}
374
375static uint64_t
376parseSpeedLimit( tr_torrent *    tor,
377                 const uint8_t * buf,
378                 uint32_t        len )
379{
380    uint64_t ret = 0;
381
382    if( len == FR_SPEED_LEN )
383    {
384        uint8_t  i8;
385        uint16_t i16;
386
387        readBytes( &i16, &buf, sizeof( i16 ) );
388        tr_torrentSetSpeedLimit( tor, TR_DOWN, i16 );
389        readBytes( &i8, &buf, sizeof( i8 ) );
390        tr_torrentSetSpeedMode( tor, TR_DOWN, (tr_speedlimit)i8 );
391        readBytes( &i16, &buf, sizeof( i16 ) );
392        tr_torrentSetSpeedLimit( tor, TR_UP, i16 );
393        readBytes( &i8, &buf, sizeof( i8 ) );
394        tr_torrentSetSpeedMode( tor, TR_UP, (tr_speedlimit)i8 );
395
396        ret = TR_FR_SPEEDLIMIT;
397    }
398
399    return ret;
400}
401
402static uint64_t
403parseRun( tr_torrent *    tor,
404          const uint8_t * buf,
405          uint32_t        len )
406{
407    if( len != 1 )
408        return 0;
409    tor->isRunning = *buf == 't';
410    return TR_FR_RUN;
411}
412
413static uint64_t
414parsePeers( tr_torrent *    tor,
415            const uint8_t * buf,
416            uint32_t        len )
417{
418    uint64_t ret = 0;
419
420    if( !tor->info.isPrivate )
421    {
422        int       i;
423        const int count = len / sizeof( tr_pex );
424
425        for( i = 0; i < count; ++i )
426        {
427            tr_pex pex;
428            readBytes( &pex, &buf, sizeof( tr_pex ) );
429            tr_peerMgrAddPex( tor, TR_PEER_FROM_CACHE, &pex );
430        }
431
432        tr_tordbg( tor, "Loaded %d peers from resume file", count );
433        ret = TR_FR_PEERS;
434    }
435
436    return ret;
437}
438
439static uint64_t
440parseDownloadDir( tr_torrent *    tor,
441                  const uint8_t * buf,
442                  uint32_t        len )
443{
444    uint64_t ret = 0;
445
446    if( buf && *buf && len )
447    {
448        tr_free( tor->downloadDir );
449        tor->downloadDir = tr_strndup( (char*)buf, len );
450        ret = TR_FR_DOWNLOAD_DIR;
451    }
452
453    return ret;
454}
455
456static uint64_t
457parseVersion1( tr_torrent *    tor,
458               const uint8_t * buf,
459               const uint8_t * end,
460               uint64_t        fieldsToLoad )
461{
462    uint64_t ret = 0;
463
464    while( end - buf >= 5 )
465    {
466        uint8_t  id;
467        uint32_t len;
468        readBytes( &id, &buf, sizeof( id ) );
469        readBytes( &len, &buf, sizeof( len ) );
470
471        if( buf + len > end )
472        {
473            tr_torerr( tor, "Resume file seems to be corrupt.  Skipping." );
474        }
475        else if( fieldsToLoad &
476                internalIdToPublicBitfield( id ) ) switch( id )
477            {
478                case FR_ID_DOWNLOADED:
479                    ret |= parseDownloaded( tor, buf, len ); break;
480
481                case FR_ID_UPLOADED:
482                    ret |= parseUploaded( tor, buf, len ); break;
483
484                case FR_ID_PROGRESS:
485                    ret |= parseProgress( tor, buf, len ); break;
486
487                case FR_ID_PRIORITY:
488                    ret |= parsePriorities( tor, buf, len ); break;
489
490                case FR_ID_SPEED:
491                    ret |= parseSpeedLimit( tor, buf, len ); break;
492
493                case FR_ID_RUN:
494                    ret |= parseRun( tor, buf, len ); break;
495
496                case FR_ID_CORRUPT:
497                    ret |= parseCorrupt( tor, buf, len ); break;
498
499                case FR_ID_PEERS:
500                    ret |= parsePeers( tor, buf, len ); break;
501
502                case FR_ID_MAX_PEERS:
503                    ret |= parseConnections( tor, buf, len ); break;
504
505                case FR_ID_DOWNLOAD_DIR:
506                    ret |= parseDownloadDir( tor, buf, len ); break;
507
508                default:
509                    tr_tordbg( tor, "Skipping unknown resume code %d",
510                               (int)id ); break;
511            }
512
513        buf += len;
514    }
515
516    return ret;
517}
518
519static uint8_t*
520loadResumeFile( const tr_torrent * tor,
521                size_t *           len )
522{
523    uint8_t *    ret = NULL;
524    const char * cacheDir = tr_getResumeDir( tor->session );
525    const char * hash = tor->info.hashString;
526
527    if( !ret && tor->session->tag )
528    {
529        char * path = tr_strdup_printf( "%s" TR_PATH_DELIMITER_STR "%s-%s", cacheDir, hash, tor->session->tag );
530        ret = tr_loadFile( path, len );
531        tr_free( path );
532    }
533    if( !ret )
534    {
535        char * path = tr_buildPath( cacheDir, hash, NULL );
536        ret = tr_loadFile( path, len );
537        tr_free( path );
538    }
539
540    return ret;
541}
542
543static uint64_t
544fastResumeLoadImpl( tr_torrent * tor,
545                    uint64_t     fieldsToLoad )
546{
547    uint64_t  ret = 0;
548    size_t    size = 0;
549    uint8_t * buf = loadResumeFile( tor, &size );
550
551    if( !buf )
552        /* %s is the torrent name */
553        tr_torinf( tor, _( "Couldn't read resume file" ) );
554    else
555    {
556        const uint8_t * walk = buf;
557        const uint8_t * end = walk + size;
558        if( end - walk >= 4 )
559        {
560            uint32_t version;
561            readBytes( &version, &walk, sizeof( version ) );
562            if( version == 1 )
563                ret |= parseVersion1 ( tor, walk, end, fieldsToLoad );
564            else
565                /* %s is the torrent name */
566                tr_torinf( tor, _( "Couldn't read resume file" ) );
567        }
568
569        tr_free( buf );
570    }
571
572    return ret;
573}
574
575uint64_t
576tr_fastResumeLoad( tr_torrent * tor,
577                   uint64_t     fieldsToLoad )
578{
579    return fastResumeLoadImpl( tor, fieldsToLoad );
580}
581
582void
583tr_fastResumeRemove( const tr_torrent * tor )
584{
585    const char * cacheDir = tr_getResumeDir( tor->session );
586    const char * hash = tor->info.hashString;
587
588    if( tor->session->tag )
589    {
590        char * path = tr_strdup_printf( "%s" TR_PATH_DELIMITER_STR "%s-%s", cacheDir, hash, tor->session->tag );
591        unlink( path );
592        tr_free( path );
593    }
594    else
595    {
596        char * path = tr_buildPath( cacheDir, hash, NULL );
597        unlink( path );
598        tr_free( path );
599    }
600}
601
Note: See TracBrowser for help on using the repository browser.