source: trunk/libtransmission/fastresume.c @ 5517

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

#684: Use XDG basedir spec for configuration and cache files in $HOME

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