source: trunk/libtransmission/fastresume.c @ 3387

Last change on this file since 3387 was 3387, checked in by charles, 15 years ago

fix r3379 bug that forced us to recheck the torrents every startup

  • Property svn:keywords set to Date Rev Author Id
File size: 22.2 KB
Line 
1/******************************************************************************
2 * $Id: fastresume.c 3387 2007-10-13 03:58:54Z charles $
3 *
4 * Copyright (c) 2005-2007 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 * Fast resume
27 ***********************************************************************
28 * The format of the resume file is a 4 byte format version (currently 1),
29 * followed by several variable-sized blocks of data.  Each block is
30 * preceded by a 1 byte ID and a 4 byte length.  The currently recognized
31 * IDs are defined below by the FR_ID_* macros.  The length does not include
32 * the 5 bytes for the ID and length.
33 *
34 * The name of the resume file is "resume.<hash>-<tag>", although
35 * older files with a name of "resume.<hash>" will be recognized if
36 * the former doesn't exist.
37 *
38 * All values are stored in the native endianness. Moving a
39 * libtransmission resume file from an architecture to another will not
40 * work, although it will not hurt either (the version will be wrong,
41 * so the resume file will not be read).
42 **********************************************************************/
43
44#include <assert.h>
45#include <errno.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <time.h>
50
51#include <sys/types.h>
52#include <sys/stat.h>
53#include <unistd.h>
54
55#include "transmission.h"
56#include "completion.h"
57#include "fastresume.h"
58#include "peer-mgr.h"
59#include "platform.h"
60#include "utils.h"
61
62/* time_t can be 32 or 64 bits... for consistency we'll hardwire 64 */ 
63typedef uint64_t tr_time_t; 
64
65enum
66{
67    /* deprecated */
68    FR_ID_PROGRESS_SLOTS = 1,
69
70    /* number of bytes downloaded */
71    FR_ID_DOWNLOADED = 2,
72
73    /* number of bytes uploaded */
74    FR_ID_UPLOADED = 3,
75
76    /* IPs and ports of connectable peers */
77    FR_ID_PEERS_OLD = 4,
78
79    /* progress data:
80     *  - 4 bytes * number of files: mtimes of files
81     *  - 1 bit * number of blocks: whether we have the block or not */
82    FR_ID_PROGRESS = 5,
83
84    /* dnd and priority
85     * char * number of files: l,n,h for low, normal, high priority
86     * char * number of files: t,f for DND flags */
87    FR_ID_PRIORITY = 6,
88
89    /* transfer speeds
90     * uint32_t: dl speed rate to use when the mode is single
91     * uint32_t: dl's tr_speedlimit
92     * uint32_t: ul speed rate to use when the mode is single
93     * uint32_t: ul's tr_speedlimit
94     */
95    FR_ID_SPEED = 8,
96
97    /* active
98     * char: 't' if running, 'f' if paused
99     */
100    FR_ID_RUN = 9,
101
102    /* number of corrupt bytes downloaded */
103    FR_ID_CORRUPT = 10,
104
105    /* IPs and ports of connectable peers */
106    FR_ID_PEERS = 11,
107
108    /* destination of the torrent: zero-terminated string */
109    FR_ID_DESTINATION = 12
110};
111
112
113/* macros for the length of various pieces of the progress data */
114#define FR_MTIME_LEN( t ) \
115  ( sizeof(tr_time_t) * (t)->info.fileCount )
116#define FR_BLOCK_BITFIELD_LEN( t ) \
117  ( ( (t)->blockCount + 7 ) / 8 )
118#define FR_PROGRESS_LEN( t ) \
119  ( FR_MTIME_LEN( t ) + FR_BLOCK_BITFIELD_LEN( t ) )
120#define FR_SPEED_LEN (2 * (sizeof(uint16_t) + sizeof(uint8_t) ) )
121
122static void
123fastResumeFileName( char * buf, size_t buflen, const tr_torrent * tor, int tag )
124{
125    const char * cacheDir = tr_getCacheDirectory ();
126    const char * hash = tor->info.hashString;
127
128    if( !tag )
129    {
130        tr_buildPath( buf, buflen, cacheDir, hash, NULL );
131    }
132    else
133    {
134        char base[1024];
135        snprintf( base, sizeof(base), "%s-%s", hash, tor->handle->tag );
136        tr_buildPath( buf, buflen, cacheDir, base, NULL );
137    }
138}
139
140static tr_time_t*
141getMTimes( const tr_torrent * tor, int * setme_n )
142{
143    int i;
144    const int n = tor->info.fileCount;
145    tr_time_t * m = calloc( n, sizeof(tr_time_t) );
146
147    for( i=0; i<n; ++i ) {
148        char fname[MAX_PATH_LENGTH];
149        struct stat sb;
150        tr_buildPath( fname, sizeof(fname),
151                      tor->destination, tor->info.files[i].name, NULL );
152        if ( !stat( fname, &sb ) && S_ISREG( sb.st_mode ) ) {
153#ifdef SYS_DARWIN
154            m[i] = sb.st_mtimespec.tv_sec;
155#else
156            m[i] = sb.st_mtime;
157#endif
158        }
159    }
160
161    *setme_n = n;
162    return m;
163}
164
165static void
166fastResumeWriteData( uint8_t       id,
167                     const void  * data,
168                     uint32_t      size,
169                     uint32_t      count,
170                     FILE        * file )
171{
172    uint32_t  datalen = size * count;
173
174    fwrite( &id, 1, 1, file );
175    fwrite( &datalen, 4, 1, file );
176    fwrite( data, size, count, file );
177}
178
179void
180tr_fastResumeSave( const tr_torrent * tor )
181{
182    char      path[MAX_PATH_LENGTH];
183    FILE    * file;
184    const int version = 1;
185    uint64_t  total;
186
187    fastResumeFileName( path, sizeof path, tor, 1 );
188    file = fopen( path, "wb" );
189    if( NULL == file ) {
190        tr_err( "Couldn't open '%s' for writing", path );
191        return;
192    }
193   
194    /* Write format version */
195    fwrite( &version, 4, 1, file );
196
197    if( TRUE ) /* FR_ID_DESTINATION */
198    {
199        const char * d = tor->destination ? tor->destination : "";
200        const int byteCount = strlen( d ) + 1;
201        fastResumeWriteData( FR_ID_DESTINATION, d, 1, byteCount, file );
202    }
203
204    /* Write progress data */
205    if (1) {
206        int n;
207        tr_time_t * mtimes;
208        uint8_t * buf = malloc( FR_PROGRESS_LEN( tor ) );
209        uint8_t * walk = buf;
210        const tr_bitfield * bitfield;
211
212        /* mtimes */
213        mtimes = getMTimes( tor, &n );
214        memcpy( walk, mtimes, n*sizeof(tr_time_t) );
215        walk += n * sizeof(tr_time_t);
216
217        /* completion bitfield */
218        bitfield = tr_cpBlockBitfield( tor->completion );
219        assert( (unsigned)FR_BLOCK_BITFIELD_LEN( tor ) == bitfield->len );
220        memcpy( walk, bitfield->bits, bitfield->len );
221        walk += bitfield->len;
222
223        /* write it */
224        assert( walk-buf == (int)FR_PROGRESS_LEN( tor ) );
225        fastResumeWriteData( FR_ID_PROGRESS, buf, 1, walk-buf, file );
226
227        /* cleanup */
228        free( mtimes );
229        free( buf );
230    }
231
232
233    /* Write the priorities and DND flags */
234    if( TRUE )
235    {
236        int i;
237        const int n = tor->info.fileCount;
238        char * buf = tr_new0( char, n*2 );
239        char * walk = buf;
240
241        /* priorities */
242        for( i=0; i<n; ++i ) {
243            char ch;
244            const int priority = tor->info.files[i].priority;
245            switch( priority ) {
246               case TR_PRI_LOW:   ch = 'l'; break; /* low */
247               case TR_PRI_HIGH:  ch = 'h'; break; /* high */
248               default:           ch = 'n'; break; /* normal */
249            };
250            *walk++ = ch;
251        }
252
253        /* dnd flags */
254        for( i=0; i<n; ++i )
255            *walk++ = tor->info.files[i].dnd ? 't' : 'f';
256
257        /* write it */
258        assert( walk - buf == 2*n );
259        fastResumeWriteData( FR_ID_PRIORITY, buf, 1, walk-buf, file );
260
261        /* cleanup */
262        tr_free( buf );
263    }
264
265
266    /* Write the torrent ul/dl speed caps */
267    if( TRUE )
268    {
269        const int len = FR_SPEED_LEN;
270        char * buf = tr_new0( char, len );
271        char * walk = buf;
272        uint16_t i16;
273        uint8_t i8;
274
275        i16 = (uint16_t) tr_torrentGetSpeedLimit( tor, TR_DOWN );
276        memcpy( walk, &i16, 2 ); walk += 2;
277        i8 = (uint8_t) tr_torrentGetSpeedMode( tor, TR_DOWN );
278        memcpy( walk, &i8, 1 ); walk += 1;
279        i16 = (uint16_t) tr_torrentGetSpeedLimit( tor, TR_UP );
280        memcpy( walk, &i16, 2 ); walk += 2;
281        i8 = (uint8_t) tr_torrentGetSpeedMode( tor, TR_UP );
282        memcpy( walk, &i8, 1 ); walk += 1;
283
284        assert( walk - buf == len );
285        fastResumeWriteData( FR_ID_SPEED, buf, 1, walk-buf, file );
286        tr_free( buf );
287    }
288
289    if( TRUE ) /* FR_ID_RUN */
290    {
291        const char is_running = tor->isRunning ? 't' : 'f';
292        fastResumeWriteData( FR_ID_RUN, &is_running, 1, 1, file );
293    }
294
295    /* Write download and upload totals */
296
297    total = tor->downloadedCur + tor->downloadedPrev;
298    fastResumeWriteData( FR_ID_DOWNLOADED, &total, 8, 1, file );
299
300    total = tor->uploadedCur + tor->uploadedPrev;
301    fastResumeWriteData( FR_ID_UPLOADED, &total, 8, 1, file );
302
303    total = tor->corruptCur + tor->corruptPrev;
304    fastResumeWriteData( FR_ID_CORRUPT, &total, 8, 1, file );
305
306    if( !( TR_FLAG_PRIVATE & tor->info.flags ) )
307    {
308        tr_pex * pex;
309        const int count = tr_peerMgrGetPeers( tor->handle->peerMgr,
310                                              tor->info.hash,
311                                              &pex );
312        if( count > 0 )
313            fastResumeWriteData( FR_ID_PEERS, pex, sizeof(tr_pex), count, file );
314        tr_free( pex );
315    }
316
317    fclose( file );
318
319    tr_dbg( "Resume file '%s' written", path );
320}
321
322static int
323loadDestination( tr_torrent * tor, FILE * fp )
324{
325    int pathlen = 0;
326    char path[MAX_PATH_LENGTH];
327
328    for( ;; ) {
329        const int ch = fgetc( fp );
330        if( ch==EOF ) /* end of file */
331            return TR_ERROR_IO_OTHER;
332        if( ch=='\0' ) /* end of string */
333            break;
334        path[pathlen++] = (char) ch;
335    }
336
337    path[pathlen] = '\0';
338
339    if( pathlen ) {
340        tr_free( tor->destination );
341        tor->destination = tr_strdup( path );
342    }
343
344    return TR_OK;
345}
346
347static int
348loadSpeeds( tr_torrent * tor, FILE * file )
349{
350    const size_t len = FR_SPEED_LEN;
351    char * buf = tr_new0( char, len );
352    char * walk = buf;
353    uint16_t i16;
354    uint8_t i8;
355
356    if( len != fread( buf, 1, len, file ) ) {
357        tr_inf( "Couldn't read from resume file" );
358        free( buf );
359        return TR_ERROR_IO_OTHER;
360    }
361
362    memcpy( &i16, walk, 2 ); walk += 2;
363    tr_torrentSetSpeedLimit( tor, TR_DOWN, i16 );
364    memcpy( &i8, walk, 1 ); walk += 1;
365    tr_torrentSetSpeedMode( tor, TR_DOWN, (tr_speedlimit)i8 );
366    memcpy( &i16, walk, 2 ); walk += 2;
367    tr_torrentSetSpeedLimit( tor, TR_UP, i16 );
368    memcpy( &i8, walk, 1 ); walk += 1;
369    tr_torrentSetSpeedMode( tor, TR_UP, (tr_speedlimit)i8 );
370
371    tr_free( buf );
372    return TR_OK;
373}
374
375
376static int
377loadPriorities( tr_torrent * tor,
378                FILE       * file )
379{
380    const size_t n = tor->info.fileCount;
381    const size_t len = 2 * n;
382    int *dnd = NULL, dndCount = 0;
383    int *dl = NULL, dlCount = 0;
384    char * buf = tr_new0( char, len );
385    char * walk = buf;
386    size_t i;
387
388    if( len != fread( buf, 1, len, file ) ) {
389        tr_inf( "Couldn't read from resume file" );
390        free( buf );
391        return TR_ERROR_IO_OTHER;
392    }
393
394    /* set file priorities */
395    for( i=0; i<n; ++i ) {
396       tr_priority_t priority;
397       const char ch = *walk++;
398       switch( ch ) {
399           case 'l': priority = TR_PRI_LOW; break;
400           case 'h': priority = TR_PRI_HIGH; break;
401           default:  priority = TR_PRI_NORMAL; break;
402       }
403       tor->info.files[i].priority = priority;
404    }
405
406    /* set the dnd flags */
407    dl = tr_new( int, len );
408    dnd = tr_new( int, len );
409    for( i=0; i<n; ++i )
410        if( *walk++ == 't' ) /* 't' means the DND flag is true */
411            dnd[dndCount++] = i;
412        else
413            dl[dlCount++] = i;
414
415    if( dndCount )
416        tr_torrentSetFileDLs ( tor, dnd, dndCount, FALSE );
417    if( dlCount )
418        tr_torrentSetFileDLs ( tor, dl, dlCount, TRUE );
419
420    tr_free( dnd );
421    tr_free( dl );
422    tr_free( buf );
423    return TR_OK;
424}
425
426static int
427fastResumeLoadProgress( const tr_torrent  * tor,
428                        tr_bitfield       * uncheckedPieces,
429                        FILE              * file )
430{
431    int i;
432    const size_t len = FR_PROGRESS_LEN( tor );
433    uint8_t * buf = calloc( len, 1 );
434    uint8_t * walk = buf;
435
436    if( len != fread( buf, 1, len, file ) ) {
437        tr_inf( "Couldn't read from resume file" );
438        free( buf );
439        return TR_ERROR_IO_OTHER;
440    }
441
442    /* compare file mtimes */
443    if (1) {
444        int n;
445        tr_time_t * curMTimes = getMTimes( tor, &n );
446        const tr_time_t * oldMTimes = (const tr_time_t *) walk;
447        for( i=0; i<n; ++i ) {
448            if ( curMTimes[i]!=oldMTimes[i] ) {
449                const tr_file * file = &tor->info.files[i];
450                tr_dbg( "File '%s' mtimes differ-- flagging pieces [%d..%d] for recheck",
451                        file->name, file->firstPiece, file->lastPiece);
452                tr_bitfieldAddRange( uncheckedPieces, 
453                                     file->firstPiece, file->lastPiece+1 );
454            }
455        }
456        free( curMTimes );
457        walk += n * sizeof(tr_time_t);
458    }
459
460    /* get the completion bitfield */
461    if (1) {
462        tr_bitfield bitfield;
463        memset( &bitfield, 0, sizeof bitfield );
464        bitfield.len = FR_BLOCK_BITFIELD_LEN( tor );
465        bitfield.bits = walk;
466        tr_cpBlockBitfieldSet( tor->completion, &bitfield );
467    }
468
469    /* the files whose mtimes are wrong,
470       remove from completion pending a recheck... */
471    for( i=0; i<tor->info.pieceCount; ++i )
472        if( tr_bitfieldHas( uncheckedPieces, i ) )
473            tr_cpPieceRem( tor->completion, i );
474
475
476    free( buf );
477    return TR_OK;
478}
479
480static uint64_t
481fastResumeLoadOld( tr_torrent   * tor,
482                   tr_bitfield  * uncheckedPieces, 
483                   FILE         * file )
484{
485    uint64_t ret = 0;
486
487    /* Check the size */
488    const int size = 4 + FR_PROGRESS_LEN( tor );
489    fseek( file, 0, SEEK_END );
490    if( ftell( file ) != size )
491    {
492        tr_inf( "Wrong size for resume file (%d bytes, %d expected)",
493                (int)ftell( file ), size );
494        fclose( file );
495        return 1;
496    }
497
498    /* load progress information */
499    fseek( file, 4, SEEK_SET );
500    if( fastResumeLoadProgress( tor, uncheckedPieces, file ) )
501    {
502        fclose( file );
503        return 1;
504    }
505
506    fclose( file );
507
508    ret |= TR_FR_PROGRESS;
509    tr_inf( "Fast resuming successful (version 0)" );
510
511    return ret;
512}
513
514static uint64_t
515fastResumeLoadImpl ( tr_torrent   * tor,
516                     const char   * fallbackDestination,
517                     tr_bitfield  * uncheckedPieces )
518{
519    char      path[MAX_PATH_LENGTH];
520    FILE    * file;
521    int       version = 0;
522    uint8_t   id;
523    uint32_t  len;
524    uint64_t  ret = 0;
525
526    assert( tor != NULL );
527    assert( uncheckedPieces != NULL );
528
529    /* Open resume file */
530    fastResumeFileName( path, sizeof path, tor, 1 );
531    file = fopen( path, "rb" );
532    if( !file )
533    {
534        if( ENOENT == errno )
535        {
536            fastResumeFileName( path, sizeof path, tor, 0 );
537            file = fopen( path, "rb" );
538            if( !file )
539            {
540                fastResumeFileName( path, sizeof path, tor, 1 );
541                tr_inf( "Couldn't open '%s' for reading", path );
542                return ret;
543            }
544        }
545    }
546
547    tr_dbg( "Resume file '%s' loaded", path );
548
549    /* Check format version */
550    fread( &version, 4, 1, file );
551    if( 0 == version )
552    {
553        return fastResumeLoadOld( tor, uncheckedPieces, file );
554    }
555    if( 1 != version )
556    {
557        tr_inf( "Resume file has version %d, not supported", version );
558        fclose( file );
559        return ret;
560    }
561
562    /* read each block of data */
563    while( 1 == fread( &id, 1, 1, file ) && 1 == fread( &len, 4, 1, file ) )
564    {
565fprintf( stderr, "reading id %d\n", (int)id );
566        switch( id )
567        {
568            case FR_ID_PROGRESS:
569                /* read progress data */
570                if( (uint32_t)FR_PROGRESS_LEN( tor ) == len )
571                {
572                    const int rret = fastResumeLoadProgress( tor, uncheckedPieces, file );
573
574                    if( rret && ( feof(file) || ferror(file) ) )
575                    {
576                        fclose( file );
577                        return ret;
578                    }
579
580                    ret |= TR_FR_PROGRESS;
581                    continue;
582                }
583                break;
584
585            case FR_ID_PRIORITY:
586
587                /* read priority data */
588                if( len == (uint32_t)(2 * tor->info.fileCount) )
589                {
590                    const int rret = loadPriorities( tor, file );
591
592                    if( rret && ( feof(file) || ferror(file) ) )
593                    {
594                        fclose( file );
595                        return ret;
596                    }
597
598                    ret |= TR_FR_PRIORITY;
599                    continue;
600                }
601                break;
602
603            case FR_ID_SPEED:
604                /*  read speed data */
605                if( len == FR_SPEED_LEN )
606                {
607                    const int rret = loadSpeeds( tor, file );
608
609                    if( rret && ( feof(file) || ferror(file) ) )
610                    {
611                        fclose( file );
612                        return ret;
613                    }
614
615                    ret |= TR_FR_SPEEDLIMIT;
616                    continue;
617                }
618                break;
619
620            case FR_ID_DESTINATION:
621                {
622                    const int rret = loadDestination( tor, file );
623
624                    if( rret && ( feof(file) || ferror(file) ) )
625                    {
626                        fclose( file );
627                        return ret;
628                    }
629
630                    if( tor->destination == NULL )
631                        tor->destination = tr_strdup( fallbackDestination );
632
633                    ret |= TR_FR_DESTINATION;
634                    continue;
635                }
636                break;
637           
638            case FR_ID_RUN:
639                {
640                    char ch;
641                    if( fread( &ch, 1, 1, file ) != 1 )
642                    {
643                        fclose( file );
644                        return ret;
645                    }
646                    tor->isRunning = ch=='t';
647                    ret |= TR_FR_RUN;
648                    continue;
649                }
650
651            case FR_ID_DOWNLOADED:
652                /* read download total */
653                if( 8 == len)
654                {
655                    if( 1 != fread( &tor->downloadedPrev, 8, 1, file ) )
656                    {
657                        fclose( file );
658                        return ret;
659                    }
660                    tor->downloadedCur = 0;
661                    ret |= TR_FR_DOWNLOADED;
662                    continue;
663                }
664                break;
665
666            case FR_ID_UPLOADED:
667                /* read upload total */
668                if( 8 == len)
669                {
670                    if( 1 != fread( &tor->uploadedPrev, 8, 1, file ) )
671                    {
672                        fclose( file );
673                        return ret;
674                    }
675                    tor->uploadedCur = 0;
676                    ret |= TR_FR_UPLOADED;
677                    continue;
678                }
679                break;
680
681            case FR_ID_CORRUPT:
682                /* read upload total */
683                if( 8 == len )
684                {
685                    if( 1 != fread( &tor->corruptPrev, 8, 1, file ) )
686                    {
687                        fclose( file );
688                        return ret;
689                    }
690                    tor->corruptCur = 0;
691                    ret |= TR_FR_CORRUPT;
692                    continue;
693                }
694                break;
695
696            case FR_ID_PEERS_OLD:
697                if( !( TR_FLAG_PRIVATE & tor->info.flags ) )
698                {
699                    uint8_t * buf = malloc( len );
700                    if( 1 != fread( buf, len, 1, file ) )
701                    {
702                        free( buf );
703                        fclose( file );
704                        return ret;
705                    }
706
707                    tr_peerMgrAddPeers( tor->handle->peerMgr,
708                                        tor->info.hash,
709                                        TR_PEER_FROM_CACHE,
710                                        buf, len / 6 );
711
712                    tr_dbg( "found %i peers in resume file", len/6 );
713                    free( buf );
714                    ret |= TR_FR_PEERS;
715                }
716
717            case FR_ID_PEERS:
718                if( !( TR_FLAG_PRIVATE & tor->info.flags ) )
719                {
720                    const int count = len / sizeof(tr_pex);
721                    tr_pex * pex = tr_new0( tr_pex, count );
722                    if( 1 != fread( pex, sizeof(tr_pex), count, file ) )
723                    {
724                        free( pex );
725                        fclose( file );
726                        return ret;
727                    }
728
729                    tr_peerMgrAddPex( tor->handle->peerMgr,
730                                      tor->info.hash,
731                                      TR_PEER_FROM_CACHE,
732                                      pex, count );
733
734                    tr_dbg( "found %i peers in resume file", len/6 );
735                    free( pex );
736                    ret |= TR_FR_PEERS;
737                }
738                continue;
739
740            default:
741                break;
742        }
743
744        /* if we didn't read the data, seek past it */
745        tr_inf( "Skipping resume data type %02x, %u bytes", id, len );
746        fseek( file, len, SEEK_CUR );
747    }
748fprintf( stderr, "\n\n" );
749
750    fclose( file );
751    return ret;
752}
753
754uint64_t
755tr_fastResumeLoad( tr_torrent   * tor,
756                   const char   * fallbackDestination,
757                   tr_bitfield  * uncheckedPieces )
758{
759    const uint64_t ret = fastResumeLoadImpl( tor, fallbackDestination, uncheckedPieces );
760
761    if( ! ( ret & TR_FR_PROGRESS ) )
762        tr_bitfieldAddRange( uncheckedPieces, 0, tor->info.pieceCount );
763
764    return ret;
765}
Note: See TracBrowser for help on using the repository browser.