source: trunk/libtransmission/fastresume.c @ 5081

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

remove unnecessary #includes

  • Property svn:keywords set to Date Rev Author Id
File size: 20.9 KB
Line 
1/******************************************************************************
2 * $Id: fastresume.c 5081 2008-02-19 18:39:49Z 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 * 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 <stdio.h>
46#include <stdlib.h> /* calloc */
47#include <string.h> /* strcpy, memset, memcmp */
48
49#include <sys/types.h>
50#include <sys/stat.h> /* stat */
51#include <unistd.h> /* unlink */
52
53#include "transmission.h"
54#include "completion.h"
55#include "fastresume.h"
56#include "peer-mgr.h"
57#include "platform.h"
58#include "torrent.h"
59#include "utils.h"
60
61/* time_t can be 32 or 64 bits... for consistency we'll hardwire 64 */ 
62typedef uint64_t tr_time_t; 
63
64enum
65{
66    /* number of bytes downloaded */
67    FR_ID_DOWNLOADED = 2,
68
69    /* number of bytes uploaded */
70    FR_ID_UPLOADED = 3,
71
72    /* progress data:
73     *  - 4 bytes * number of files: mtimes of files
74     *  - 1 bit * number of blocks: whether we have the block or not */
75    FR_ID_PROGRESS = 5,
76
77    /* dnd and priority
78     * char * number of files: l,n,h for low, normal, high priority
79     * char * number of files: t,f for DND flags */
80    FR_ID_PRIORITY = 6,
81
82    /* transfer speeds
83     * uint32_t: dl speed rate to use when the mode is single
84     * uint32_t: dl's tr_speedlimit
85     * uint32_t: ul speed rate to use when the mode is single
86     * uint32_t: ul's tr_speedlimit
87     */
88    FR_ID_SPEED = 8,
89
90    /* active
91     * char: 't' if running, 'f' if paused
92     */
93    FR_ID_RUN = 9,
94
95    /* number of corrupt bytes downloaded */
96    FR_ID_CORRUPT = 10,
97
98    /* IPs and ports of connectable peers */
99    FR_ID_PEERS = 11,
100
101    /* destination of the torrent: zero-terminated string */
102    FR_ID_DESTINATION = 12,
103
104    /* pex flag
105     * 't' if pex is enabled, 'f' if disabled */
106    FR_ID_PEX = 13,
107
108    /* max connected peers -- uint16_t */
109    FR_ID_MAX_PEERS = 14
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 i, 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        for( i=0; i<n; ++i )
215            if( !tr_torrentIsFileChecked( tor, i ) )
216                mtimes[i] = ~(tr_time_t)0; /* force a recheck next time */
217        memcpy( walk, mtimes, n*sizeof(tr_time_t) );
218        walk += n * sizeof(tr_time_t);
219
220        /* completion bitfield */
221        bitfield = tr_cpBlockBitfield( tor->completion );
222        assert( (unsigned)FR_BLOCK_BITFIELD_LEN( tor ) == bitfield->len );
223        memcpy( walk, bitfield->bits, bitfield->len );
224        walk += bitfield->len;
225
226        /* write it */
227        assert( walk-buf == (int)FR_PROGRESS_LEN( tor ) );
228        fastResumeWriteData( FR_ID_PROGRESS, buf, 1, walk-buf, file );
229
230        /* cleanup */
231        free( mtimes );
232        free( buf );
233    }
234
235
236    /* Write the priorities and DND flags */
237    if( TRUE )
238    {
239        int i;
240        const int n = tor->info.fileCount;
241        char * buf = tr_new0( char, n*2 );
242        char * walk = buf;
243
244        /* priorities */
245        for( i=0; i<n; ++i ) {
246            char ch;
247            const int priority = tor->info.files[i].priority;
248            switch( priority ) {
249               case TR_PRI_LOW:   ch = 'l'; break; /* low */
250               case TR_PRI_HIGH:  ch = 'h'; break; /* high */
251               default:           ch = 'n'; break; /* normal */
252            };
253            *walk++ = ch;
254        }
255
256        /* dnd flags */
257        for( i=0; i<n; ++i )
258            *walk++ = tor->info.files[i].dnd ? 't' : 'f';
259
260        /* write it */
261        assert( walk - buf == 2*n );
262        fastResumeWriteData( FR_ID_PRIORITY, buf, 1, walk-buf, file );
263
264        /* cleanup */
265        tr_free( buf );
266    }
267
268
269    /* Write the torrent ul/dl speed caps */
270    if( TRUE )
271    {
272        const int len = FR_SPEED_LEN;
273        char * buf = tr_new0( char, len );
274        char * walk = buf;
275        uint16_t i16;
276        uint8_t i8;
277
278        i16 = (uint16_t) tr_torrentGetSpeedLimit( tor, TR_DOWN );
279        memcpy( walk, &i16, 2 ); walk += 2;
280        i8 = (uint8_t) tr_torrentGetSpeedMode( tor, TR_DOWN );
281        memcpy( walk, &i8, 1 ); walk += 1;
282        i16 = (uint16_t) tr_torrentGetSpeedLimit( tor, TR_UP );
283        memcpy( walk, &i16, 2 ); walk += 2;
284        i8 = (uint8_t) tr_torrentGetSpeedMode( tor, TR_UP );
285        memcpy( walk, &i8, 1 ); walk += 1;
286
287        assert( walk - buf == len );
288        fastResumeWriteData( FR_ID_SPEED, buf, 1, walk-buf, file );
289        tr_free( buf );
290    }
291
292    if( TRUE ) /* FR_ID_RUN */
293    {
294        const char is_running = tor->isRunning ? 't' : 'f';
295        fastResumeWriteData( FR_ID_RUN, &is_running, 1, 1, file );
296    }
297
298    /* Write download and upload totals */
299
300    total = tor->downloadedCur + tor->downloadedPrev;
301    fastResumeWriteData( FR_ID_DOWNLOADED, &total, 8, 1, file );
302
303    total = tor->uploadedCur + tor->uploadedPrev;
304    fastResumeWriteData( FR_ID_UPLOADED, &total, 8, 1, file );
305
306    total = tor->corruptCur + tor->corruptPrev;
307    fastResumeWriteData( FR_ID_CORRUPT, &total, 8, 1, file );
308
309    fastResumeWriteData( FR_ID_MAX_PEERS,
310                         &tor->maxConnectedPeers,
311                         sizeof(uint16_t), 1, file );
312
313    if( !tor->info.isPrivate )
314    {
315        tr_pex * pex;
316        const int count = tr_peerMgrGetPeers( tor->handle->peerMgr,
317                                              tor->info.hash,
318                                              &pex );
319        if( count > 0 )
320            fastResumeWriteData( FR_ID_PEERS, pex, sizeof(tr_pex), count, file );
321        tr_free( pex );
322    }
323
324    fclose( file );
325
326    /*tr_dbg( "Wrote resume file for '%s'", tor->info.name );*/
327}
328
329/***
330****
331***/
332
333static uint64_t
334internalIdToPublicBitfield( uint8_t id )
335{
336    uint64_t ret = 0;
337
338    switch( id )
339    {
340        case FR_ID_DOWNLOADED:     ret = TR_FR_DOWNLOADED;    break;
341        case FR_ID_UPLOADED:       ret = TR_FR_UPLOADED;      break;
342        case FR_ID_PROGRESS:       ret = TR_FR_PROGRESS;      break;
343        case FR_ID_PRIORITY:       ret = TR_FR_PRIORITY;      break;
344        case FR_ID_SPEED:          ret = TR_FR_SPEEDLIMIT;    break;
345        case FR_ID_RUN:            ret = TR_FR_RUN;           break;
346        case FR_ID_CORRUPT:        ret = TR_FR_CORRUPT;       break;
347        case FR_ID_PEERS:          ret = TR_FR_PEERS;         break;
348        case FR_ID_DESTINATION:    ret = TR_FR_DESTINATION;   break;
349        case FR_ID_MAX_PEERS:      ret = TR_FR_MAX_PEERS;     break;
350    }
351
352    return ret;
353}
354
355static void
356readBytes( void * target, const uint8_t ** source, size_t byteCount )
357{
358    memcpy( target, *source, byteCount );
359    *source += byteCount;
360}
361
362static uint64_t
363parseDownloaded( tr_torrent * tor, const uint8_t * buf, uint32_t len )
364{
365    if( len != sizeof(uint64_t) )
366        return 0;
367    readBytes( &tor->downloadedPrev, &buf, sizeof(uint64_t) );
368    return TR_FR_DOWNLOADED;
369}
370
371static uint64_t
372parseUploaded( tr_torrent * tor, const uint8_t * buf, uint32_t len )
373{
374    if( len != sizeof(uint64_t) )
375        return 0;
376    readBytes( &tor->uploadedPrev, &buf, sizeof(uint64_t) );
377    return TR_FR_UPLOADED;
378}
379
380static uint64_t
381parseCorrupt( tr_torrent * tor, const uint8_t * buf, uint32_t len )
382{
383    if( len != sizeof(uint64_t) )
384        return 0;
385    readBytes( &tor->corruptPrev, &buf, sizeof(uint64_t) );
386    return TR_FR_CORRUPT;
387}
388
389static uint64_t
390parseConnections( tr_torrent * tor, const uint8_t * buf, uint32_t len )
391{
392    if( len != sizeof(uint16_t) )
393        return 0;
394    readBytes( &tor->maxConnectedPeers, &buf, sizeof(uint16_t) );
395    return TR_FR_MAX_PEERS;
396}
397
398static uint64_t
399parseProgress( tr_torrent     * tor,
400               const uint8_t  * buf,
401               uint32_t         len )
402{
403    int i;
404    uint64_t ret = 0;
405   
406    if( len == FR_PROGRESS_LEN( tor ) )
407    {
408        int n;
409        tr_bitfield bitfield;
410
411        /* compare file mtimes */
412        tr_time_t * curMTimes = getMTimes( tor, &n );
413        const uint8_t * walk = buf;
414        tr_time_t mtime;
415        for( i=0; i<n; ++i ) {
416            readBytes( &mtime, &walk, sizeof(tr_time_t) );
417            if ( curMTimes[i] == mtime )
418                tr_torrentSetFileChecked( tor, i, TRUE );
419            else {
420                tr_torrentSetFileChecked( tor, i, FALSE );
421                tr_dbg( "File '%s' recheck needed", tor->info.files[i].name );
422            }
423        }
424        free( curMTimes );
425
426        /* get the completion bitfield */
427        memset( &bitfield, 0, sizeof bitfield );
428        bitfield.len = FR_BLOCK_BITFIELD_LEN( tor );
429        bitfield.bits = (uint8_t*) walk;
430        tr_cpBlockBitfieldSet( tor->completion, &bitfield );
431
432        ret = TR_FR_PROGRESS;
433    }
434
435    /* the files whose mtimes are wrong,
436       remove from completion pending a recheck... */
437    for( i=0; i<tor->info.pieceCount; ++i )
438        if( !tr_torrentIsPieceChecked( tor, i ) )
439            tr_cpPieceRem( tor->completion, i );
440
441    return ret;
442}
443
444static uint64_t
445parsePriorities( tr_torrent * tor, const uint8_t * buf, uint32_t len )
446{
447    uint64_t ret = 0;
448
449    if( len == (uint32_t)(2 * tor->info.fileCount) )
450    {
451        const size_t n = tor->info.fileCount;
452        const size_t len = 2 * n;
453        int *dnd = NULL, dndCount = 0;
454        int *dl = NULL, dlCount = 0;
455        size_t i;
456        const uint8_t * walk = buf;
457
458        /* set file priorities */
459        for( i=0; i<n; ++i ) {
460           tr_priority_t priority;
461           const char ch = *walk++;
462           switch( ch ) {
463               case 'l': priority = TR_PRI_LOW; break;
464               case 'h': priority = TR_PRI_HIGH; break;
465               default:  priority = TR_PRI_NORMAL; break;
466           }
467           tr_torrentInitFilePriority( tor, i, priority );
468        }
469
470        /* set the dnd flags */
471        dl = tr_new( int, len );
472        dnd = tr_new( int, len );
473        for( i=0; i<n; ++i )
474            if( *walk++ == 't' ) /* 't' means the DND flag is true */
475                dnd[dndCount++] = i;
476            else
477                dl[dlCount++] = i;
478
479        if( dndCount )
480            tr_torrentInitFileDLs ( tor, dnd, dndCount, FALSE );
481        if( dlCount )
482            tr_torrentInitFileDLs ( tor, dl, dlCount, TRUE );
483
484        tr_free( dnd );
485        tr_free( dl );
486
487        ret = TR_FR_PRIORITY;
488    }
489
490    return ret;
491}
492
493static uint64_t
494parseSpeedLimit( tr_torrent * tor, const uint8_t * buf, uint32_t len )
495{
496    uint64_t ret = 0;
497
498    if( len == FR_SPEED_LEN )
499    {
500        uint8_t i8;
501        uint16_t i16;
502
503        readBytes( &i16, &buf, sizeof(i16) );
504        tr_torrentSetSpeedLimit( tor, TR_DOWN, i16 );
505        readBytes( &i8, &buf, sizeof(i8) );
506        tr_torrentSetSpeedMode( tor, TR_DOWN, (tr_speedlimit)i8 );
507        readBytes( &i16, &buf, sizeof(i16) );
508        tr_torrentSetSpeedLimit( tor, TR_UP, i16 );
509        readBytes( &i8, &buf, sizeof(i8) );
510        tr_torrentSetSpeedMode( tor, TR_UP, (tr_speedlimit)i8 );
511
512        ret = TR_FR_SPEEDLIMIT;
513    }
514
515    return ret;
516}
517
518static uint64_t
519parseRun( tr_torrent * tor, const uint8_t * buf, uint32_t len )
520{
521    if( len != 1 )
522        return 0;
523    tor->isRunning = *buf=='t';
524    return TR_FR_RUN;
525}
526
527static uint64_t
528parsePeers( tr_torrent * tor, const uint8_t * buf, uint32_t len )
529{
530    uint64_t ret = 0;
531
532    if( !tor->info.isPrivate )
533    {
534        int i;
535        const int count = len / sizeof(tr_pex);
536
537        for( i=0; i<count; ++i )
538        {
539            tr_pex pex;
540            readBytes( &pex, &buf, sizeof( tr_pex ) );
541            tr_peerMgrAddPex( tor->handle->peerMgr, tor->info.hash, TR_PEER_FROM_CACHE, &pex );
542        }
543
544        tr_dbg( "found %i peers in resume file", count );
545        ret = TR_FR_PEERS;
546    }
547
548    return ret;
549}
550
551static uint64_t
552parseDestination( tr_torrent * tor, const uint8_t * buf, uint32_t len )
553{
554    uint64_t ret = 0;
555
556    if( buf && *buf && len ) {
557        tr_free( tor->destination );
558        tor->destination = tr_strndup( (char*)buf, len );
559        ret = TR_FR_DESTINATION;
560    }
561
562    return ret;
563}
564
565static uint64_t
566parseVersion1( tr_torrent * tor, const uint8_t * buf, const uint8_t * end,
567               uint64_t fieldsToLoad )
568{
569    uint64_t ret = 0;
570
571    while( end-buf >= 5 )
572    {
573        uint8_t id;
574        uint32_t len;
575        readBytes( &id, &buf, sizeof(id) );
576        readBytes( &len, &buf, sizeof(len) );
577
578        if( fieldsToLoad & internalIdToPublicBitfield( id ) ) switch( id )
579        {
580            case FR_ID_DOWNLOADED:   ret |= parseDownloaded( tor, buf, len ); break;
581            case FR_ID_UPLOADED:     ret |= parseUploaded( tor, buf, len ); break;
582            case FR_ID_PROGRESS:     ret |= parseProgress( tor, buf, len ); break;
583            case FR_ID_PRIORITY:     ret |= parsePriorities( tor, buf, len ); break;
584            case FR_ID_SPEED:        ret |= parseSpeedLimit( tor, buf, len ); break;
585            case FR_ID_RUN:          ret |= parseRun( tor, buf, len ); break;
586            case FR_ID_CORRUPT:      ret |= parseCorrupt( tor, buf, len ); break;
587            case FR_ID_PEERS:        ret |= parsePeers( tor, buf, len ); break;
588            case FR_ID_MAX_PEERS:    ret |= parseConnections( tor, buf, len ); break;
589            case FR_ID_DESTINATION:  ret |= parseDestination( tor, buf, len ); break;
590            default:                 tr_dbg( "Skipping unknown resume code %d", (int)id ); break;
591        }
592
593        buf += len;
594    }
595
596    return ret;
597}
598
599static uint8_t* 
600loadResumeFile( const tr_torrent * tor, size_t * len )
601{
602    uint8_t * ret = NULL;
603    char path[MAX_PATH_LENGTH];
604    const char * cacheDir = tr_getCacheDirectory ();
605    const char * hash = tor->info.hashString;
606
607    if( !ret && tor->handle->tag )
608    {
609        char base[1024];
610        snprintf( base, sizeof(base), "%s-%s", hash, tor->handle->tag );
611        tr_buildPath( path, sizeof(path), cacheDir, base, NULL );
612        ret = tr_loadFile( path, len );
613    }
614
615    if( !ret )
616    {
617        tr_buildPath( path, sizeof(path), cacheDir, hash, NULL );
618        ret = tr_loadFile( path, len );
619    }
620
621    return ret;
622}
623
624static uint64_t
625fastResumeLoadImpl ( tr_torrent   * tor,
626                     uint64_t       fieldsToLoad )
627{
628    uint64_t ret = 0;
629    size_t size = 0;
630    uint8_t * buf = loadResumeFile( tor, &size );
631
632    if( !buf )
633        tr_inf( "Couldn't read resume file for '%s'", tor->info.name );
634    else {
635        const uint8_t * walk = buf;
636        const uint8_t * end = walk + size;
637        if( end - walk >= 4 ) {
638            uint32_t version;
639            readBytes( &version, &walk, sizeof(version) );
640            if( version == 1 )
641                ret |= parseVersion1 ( tor, walk, end, fieldsToLoad );
642            else
643                tr_inf( "Unsupported resume file %d for '%s'", version, tor->info.name );
644        }
645
646        tr_free( buf );
647    }
648
649    return ret;
650}
651
652static uint64_t
653setFromCtor( tr_torrent * tor, uint64_t fields, const tr_ctor * ctor, int mode )
654{
655    uint64_t ret = 0;
656
657    if( fields & TR_FR_RUN ) {
658        uint8_t isPaused;
659        if( !tr_ctorGetPaused( ctor, mode, &isPaused ) ) {
660            tor->isRunning = !isPaused;
661            ret |= TR_FR_RUN;
662        }
663    }
664
665    if( fields & TR_FR_MAX_PEERS ) 
666        if( !tr_ctorGetMaxConnectedPeers( ctor, mode, &tor->maxConnectedPeers ) )
667            ret |= TR_FR_MAX_PEERS;
668
669    if( fields & TR_FR_DESTINATION ) {
670        const char * destination;
671        if( !tr_ctorGetDestination( ctor, mode, &destination ) ) {
672            ret |= TR_FR_DESTINATION;
673            tr_free( tor->destination );
674            tor->destination = tr_strdup( destination );
675        }
676    }
677
678    return ret;
679}
680
681static uint64_t
682useManditoryFields( tr_torrent * tor, uint64_t fields, const tr_ctor * ctor )
683{
684    return setFromCtor( tor, fields, ctor, TR_FORCE );
685}
686
687static uint64_t
688useFallbackFields( tr_torrent * tor, uint64_t fields, const tr_ctor * ctor )
689{
690    return setFromCtor( tor, fields, ctor, TR_FALLBACK );
691}
692
693uint64_t
694tr_fastResumeLoad( tr_torrent     * tor,
695                   uint64_t         fieldsToLoad,
696                   const tr_ctor  * ctor )
697{
698    uint64_t ret = 0;
699
700    ret |= useManditoryFields( tor, fieldsToLoad, ctor );
701    fieldsToLoad &= ~ret;
702    ret |= fastResumeLoadImpl( tor, fieldsToLoad );
703    fieldsToLoad &= ~ret;
704    ret |= useFallbackFields( tor, fieldsToLoad, ctor );
705
706    return ret;
707}
708
709void
710tr_fastResumeRemove( const tr_torrent * tor )
711{
712    char path[MAX_PATH_LENGTH];
713    const char * cacheDir = tr_getCacheDirectory ();
714    const char * hash = tor->info.hashString;
715
716    if( tor->handle->tag )
717    {
718        char base[1024];
719        snprintf( base, sizeof(base), "%s-%s", hash, tor->handle->tag );
720        tr_buildPath( path, sizeof(path), cacheDir, base, NULL );
721        unlink( path );
722    }
723    else
724    {
725        tr_buildPath( path, sizeof(path), cacheDir, hash, NULL );
726        unlink( path );
727    }
728}
Note: See TracBrowser for help on using the repository browser.