source: trunk/libtransmission/fastresume.h @ 1603

Last change on this file since 1603 was 1603, checked in by joshe, 15 years ago

Show peers found and used when adding new peers.
Accept --without-foo in addition to --disable-foo in the configure script.

  • Property svn:keywords set to Date Rev Author Id
File size: 11.9 KB
Line 
1/******************************************************************************
2 * $Id: fastresume.h 1603 2007-03-29 00:19:09Z joshe $
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>".
35 *
36 * All values are stored in the native endianness. Moving a
37 * libtransmission resume file from an architecture to another will not
38 * work, although it will not hurt either (the version will be wrong,
39 * so the resume file will not be read).
40 **********************************************************************/
41
42/* progress data:
43 *  - 4 bytes * number of files: mtimes of files
44 *  - 1 bit * number of blocks: whether we have the block or not
45 *  - 4 bytes * number of pieces (byte aligned): the pieces that have
46 *    been completed or started in each slot
47 */
48#define FR_ID_PROGRESS          0x01
49/* number of bytes downloaded */
50#define FR_ID_DOWNLOADED        0x02
51/* number of bytes uploaded */
52#define FR_ID_UPLOADED          0x03
53/* IPs and ports of connectable peers */
54#define FR_ID_PEERS             0x04
55
56/* macros for the length of various pieces of the progress data */
57#define FR_MTIME_LEN( t ) \
58  ( 4 * (t)->info.fileCount )
59#define FR_BLOCK_BITFIELD_LEN( t ) \
60  ( ( (t)->blockCount + 7 ) / 8 )
61#define FR_SLOTPIECE_LEN( t ) \
62  ( 4 * (t)->info.pieceCount )
63#define FR_PROGRESS_LEN( t ) \
64  ( FR_MTIME_LEN( t ) + FR_BLOCK_BITFIELD_LEN( t ) + FR_SLOTPIECE_LEN( t ) )
65
66static char * fastResumeFileName( tr_io_t * io )
67{
68    char * ret;
69
70    asprintf( &ret, "%s/resume.%s", tr_getCacheDirectory(),
71              io->tor->info.hashString );
72
73    return ret;
74}
75
76static int fastResumeMTimes( tr_io_t * io, int * tab )
77{
78    tr_torrent_t * tor = io->tor;
79    tr_info_t    * inf = &tor->info;
80
81    int           i;
82    char        * path;
83    struct stat   sb;
84
85    for( i = 0; i < inf->fileCount; i++ )
86    {
87        asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
88        if( stat( path, &sb ) )
89        {
90            tab[i] = 0xFFFFFFFF;
91        }
92        else if( S_ISREG( sb.st_mode ) )
93        {
94#ifdef SYS_DARWIN
95            tab[i] = ( sb.st_mtimespec.tv_sec & 0x7FFFFFFF );
96#else
97            tab[i] = ( sb.st_mtime & 0x7FFFFFFF );
98#endif
99        }
100        else
101        {
102            /* Empty folder */
103            tab[i] = 0;
104        }
105        free( path );
106    }
107
108    return 0;
109}
110
111static inline void fastResumeWriteData( uint8_t id, void * data, uint32_t size,
112                                        uint32_t count, FILE * file )
113{
114    uint32_t  datalen = size * count;
115
116    fwrite( &id, 1, 1, file );
117    fwrite( &datalen, 4, 1, file );
118    fwrite( data, size, count, file );
119}
120
121static void fastResumeSave( tr_io_t * io )
122{
123    tr_torrent_t * tor = io->tor;
124   
125    FILE    * file;
126    int       version = 1;
127    char    * path;
128    uint8_t * buf;
129    uint64_t  total;
130    int       size;
131    tr_bitfield_t * bitfield;
132
133    buf = malloc( FR_PROGRESS_LEN( tor ) );
134
135    /* Get file sizes */
136    if( fastResumeMTimes( io, (int*)buf ) )
137    {
138        free( buf );
139        return;
140    }
141
142    /* Create/overwrite the resume file */
143    path = fastResumeFileName( io );
144    if( !( file = fopen( path, "w" ) ) )
145    {
146        tr_err( "Could not open '%s' for writing", path );
147        free( buf );
148        free( path );
149        return;
150    }
151   
152    /* Write format version */
153    fwrite( &version, 4, 1, file );
154
155    /* Build and copy the bitfield for blocks */
156    bitfield = tr_cpBlockBitfield( tor->completion );
157    assert( FR_BLOCK_BITFIELD_LEN( tor ) == bitfield->len );
158    memcpy(buf + FR_MTIME_LEN( tor ), bitfield->bits, bitfield->len );
159
160    /* Copy the 'slotPiece' table */
161    memcpy(buf + FR_MTIME_LEN( tor ) + FR_BLOCK_BITFIELD_LEN( tor ),
162           io->slotPiece, FR_SLOTPIECE_LEN( tor ) );
163
164    /* Write progress data */
165    fastResumeWriteData( FR_ID_PROGRESS, buf, 1, FR_PROGRESS_LEN( tor ), file );
166    free( buf );
167
168    /* Write download and upload totals */
169    total = tor->downloadedCur + tor->downloadedPrev;
170    fastResumeWriteData( FR_ID_DOWNLOADED, &total, 8, 1, file );
171    total = tor->uploadedCur + tor->uploadedPrev;
172    fastResumeWriteData( FR_ID_UPLOADED, &total, 8, 1, file );
173
174    if( !( TR_FLAG_PRIVATE & tor->info.flags ) )
175    {
176        /* Write IPs and ports of connectable peers, if any */
177        if( ( size = tr_peerGetConnectable( tor, &buf ) ) > 0 )
178        {
179            fastResumeWriteData( FR_ID_PEERS, buf, size, 1, file );
180            free( buf );
181        }
182    }
183
184    fclose( file );
185
186    tr_dbg( "Resume file '%s' written", path );
187    free( path );
188}
189
190static int fastResumeLoadProgress( tr_io_t * io, FILE * file )
191{
192    tr_torrent_t * tor = io->tor;
193    tr_info_t    * inf = &tor->info;
194
195    int     * fileMTimes;
196    int       i, j;
197    uint8_t * buf;
198    size_t    len;
199    tr_bitfield_t bitfield;
200
201    len = FR_PROGRESS_LEN( tor );
202    buf = calloc( len, 1 );
203    if( len != fread( buf, 1, len, file ) )
204    {
205        tr_inf( "Could not read from resume file" );
206        free( buf );
207        return 1;
208    }
209
210    /* Compare file mtimes */
211    fileMTimes = malloc( FR_MTIME_LEN( tor ) );
212    if( fastResumeMTimes( io, fileMTimes ) )
213    {
214        free( buf );
215        free( fileMTimes );
216        return 1;
217    }
218    if( memcmp( fileMTimes, buf, FR_MTIME_LEN( tor ) ) )
219    {
220        tr_inf( "File mtimes don't match" );
221        free( buf );
222        free( fileMTimes );
223        return 1;
224    }
225    free( fileMTimes );
226
227    /* Copy the bitfield for blocks and fill blockHave */
228    memset( &bitfield, 0, sizeof bitfield );
229    bitfield.len = FR_BLOCK_BITFIELD_LEN( tor );
230    bitfield.bits = buf + FR_MTIME_LEN( tor );
231    tr_cpBlockBitfieldSet( tor->completion, &bitfield );
232
233    /* Copy the 'slotPiece' table */
234    memcpy( io->slotPiece, buf + FR_MTIME_LEN( tor ) +
235            FR_BLOCK_BITFIELD_LEN( tor ), FR_SLOTPIECE_LEN( tor ) );
236
237    free( buf );
238
239    /* Update io->pieceSlot, io->slotsUsed, and tor->bitfield */
240    io->slotsUsed = 0;
241    for( i = 0; i < inf->pieceCount; i++ )
242    {
243        io->pieceSlot[i] = -1;
244        for( j = 0; j < inf->pieceCount; j++ )
245        {
246            if( io->slotPiece[j] == i )
247            {
248                // tr_dbg( "Has piece %d in slot %d", i, j );
249                io->pieceSlot[i] = j;
250                io->slotsUsed = MAX( io->slotsUsed, j + 1 );
251                break;
252            }
253        }
254    }
255    // tr_dbg( "Slot used: %d", io->slotsUsed );
256
257    return 0;
258}
259
260static int fastResumeLoadOld( tr_io_t * io, FILE * file )
261{
262    tr_torrent_t * tor = io->tor;
263   
264    int size;
265
266    /* Check the size */
267    size = 4 + FR_PROGRESS_LEN( tor );
268    fseek( file, 0, SEEK_END );
269    if( ftell( file ) != size )
270    {
271        tr_inf( "Wrong size for resume file (%d bytes, %d expected)",
272                (int)ftell( file ), size );
273        fclose( file );
274        return 1;
275    }
276
277    /* load progress information */
278    fseek( file, 4, SEEK_SET );
279    if( fastResumeLoadProgress( io, file ) )
280    {
281        fclose( file );
282        return 1;
283    }
284
285    fclose( file );
286
287    tr_inf( "Fast resuming successful (version 0)" );
288   
289    return 0;
290}
291
292static int fastResumeLoad( tr_io_t * io )
293{
294    tr_torrent_t * tor = io->tor;
295   
296    FILE    * file;
297    int       version = 0;
298    char    * path;
299    uint8_t   id;
300    uint32_t  len;
301    int       ret;
302
303    /* Open resume file */
304    path = fastResumeFileName( io );
305    if( !( file = fopen( path, "r" ) ) )
306    {
307        tr_inf( "Could not open '%s' for reading", path );
308        free( path );
309        return 1;
310    }
311    tr_dbg( "Resume file '%s' loaded", path );
312    free( path );
313
314    /* Check format version */
315    fread( &version, 4, 1, file );
316    if( 0 == version )
317    {
318        return fastResumeLoadOld( io, file );
319    }
320    if( 1 != version )
321    {
322        tr_inf( "Resume file has version %d, not supported", version );
323        fclose( file );
324        return 1;
325    }
326
327    ret = 1;
328    /* read each block of data */
329    while( 1 == fread( &id, 1, 1, file ) && 1 == fread( &len, 4, 1, file ) )
330    {
331        switch( id )
332        {
333            case FR_ID_PROGRESS:
334                /* read progress data */
335                if( (uint32_t)FR_PROGRESS_LEN( tor ) == len )
336                {
337                    if( fastResumeLoadProgress( io, file ) )
338                    {
339                        if( feof( file ) || ferror( file ) )
340                        {
341                            fclose( file );
342                            return 1;
343                        }
344                    }
345                    else
346                    {
347                        ret = 0;
348                    }
349                    continue;
350                }
351                break;
352
353            case FR_ID_DOWNLOADED:
354                /* read download total */
355                if( 8 == len)
356                {
357                    if( 1 != fread( &tor->downloadedPrev, 8, 1, file ) )
358                    {
359                        fclose( file );
360                        return 1;
361                    }
362                    continue;
363                }
364                break;
365
366            case FR_ID_UPLOADED:
367                /* read upload total */
368                if( 8 == len)
369                {
370                    if( 1 != fread( &tor->uploadedPrev, 8, 1, file ) )
371                    {
372                        fclose( file );
373                        return 1;
374                    }
375                    continue;
376                }
377                break;
378
379            case FR_ID_PEERS:
380                if( !( TR_FLAG_PRIVATE & tor->info.flags ) )
381                {
382                    int used;
383                    uint8_t * buf = malloc( len );
384                    if( 1 != fread( buf, len, 1, file ) )
385                    {
386                        free( buf );
387                        fclose( file );
388                        return 1;
389                    }
390                    used = tr_torrentAddCompact( tor, TR_PEER_FROM_CACHE,
391                                                 buf, len / 6 );
392                    tr_dbg( "found %i peers in resume file, used %i",
393                            len / 6, used );
394                    free( buf );
395                }
396                continue;
397
398            default:
399                break;
400        }
401
402        /* if we didn't read the data, seek past it */
403        tr_inf( "Skipping resume data type %02x, %u bytes", id, len );
404        fseek( file, len, SEEK_CUR );
405    }
406
407    fclose( file );
408
409    if( !ret )
410    {
411        tr_inf( "Fast resuming successful" );
412    }
413   
414    return ret;
415}
Note: See TracBrowser for help on using the repository browser.