source: trunk/libtransmission/fastresume.h @ 346

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

Fix bug where upload and download totals were discarded when fastresume failed.

  • Property svn:keywords set to Date Rev Author Id
File size: 10.5 KB
Line 
1/******************************************************************************
2 * $Id: fastresume.h 326 2006-06-11 04:32:41Z titer $
3 *
4 * Copyright (c) 2005-2006 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
54/* macros for the length of various pieces of the progress data */
55#define FR_MTIME_LEN( t ) \
56  ( 4 * (t)->info.fileCount )
57#define FR_BLOCK_BITFIELD_LEN( t ) \
58  ( ( (t)->blockCount + 7 ) / 8 )
59#define FR_SLOTPIECE_LEN( t ) \
60  ( 4 * (t)->info.pieceCount )
61#define FR_PROGRESS_LEN( t ) \
62  ( FR_MTIME_LEN( t ) + FR_BLOCK_BITFIELD_LEN( t ) + FR_SLOTPIECE_LEN( t ) )
63
64static char * fastResumeFileName( tr_io_t * io )
65{
66    char * ret;
67
68    asprintf( &ret, "%s/resume.%s", tr_getCacheDirectory(),
69              io->tor->info.hashString );
70
71    return ret;
72}
73
74static int fastResumeMTimes( tr_io_t * io, int * tab )
75{
76    tr_torrent_t * tor = io->tor;
77    tr_info_t    * inf = &tor->info;
78
79    int           i;
80    char        * path;
81    struct stat   sb;
82
83    for( i = 0; i < inf->fileCount; i++ )
84    {
85        asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
86        if( stat( path, &sb ) )
87        {
88            tr_err( "Could not stat '%s'", path );
89            free( path );
90            return 1;
91        }
92        if( ( sb.st_mode & S_IFMT ) != S_IFREG )
93        {
94            tr_err( "Wrong st_mode for '%s'", path );
95            free( path );
96            return 1;
97        }
98        free( path );
99
100#ifdef SYS_DARWIN
101        tab[i] = ( sb.st_mtimespec.tv_sec & 0x7FFFFFFF );
102#else
103        tab[i] = ( sb.st_mtime & 0x7FFFFFFF );
104#endif
105    }
106
107    return 0;
108}
109
110static inline void fastResumeWriteData( uint8_t id, void * data, uint32_t size,
111                                        uint32_t count, FILE * file )
112{
113    uint32_t  datalen = size * count;
114
115    fwrite( &id, 1, 1, file );
116    fwrite( &datalen, 4, 1, file );
117    fwrite( data, size, count, file );
118}
119
120static void fastResumeSave( tr_io_t * io )
121{
122    tr_torrent_t * tor = io->tor;
123   
124    FILE    * file;
125    int       version = 1;
126    char    * path;
127    uint8_t * buf;
128
129    buf = malloc( FR_PROGRESS_LEN( tor ) );
130
131    /* Get file sizes */
132    if( fastResumeMTimes( io, (int*)buf ) )
133    {
134        free( buf );
135        return;
136    }
137
138    /* Create/overwrite the resume file */
139    path = fastResumeFileName( io );
140    if( !( file = fopen( path, "w" ) ) )
141    {
142        tr_err( "Could not open '%s' for writing", path );
143        free( buf );
144        free( path );
145        return;
146    }
147   
148    /* Write format version */
149    fwrite( &version, 4, 1, file );
150
151    /* Build and copy the bitfield for blocks */
152    memcpy(buf + FR_MTIME_LEN( tor ), tr_cpBlockBitfield( tor->completion ),
153           FR_BLOCK_BITFIELD_LEN( tor ) );
154
155    /* Copy the 'slotPiece' table */
156    memcpy(buf + FR_MTIME_LEN( tor ) + FR_BLOCK_BITFIELD_LEN( tor ),
157           io->slotPiece, FR_SLOTPIECE_LEN( tor ) );
158
159    /* Write progress data */
160    fastResumeWriteData( FR_ID_PROGRESS, buf, 1, FR_PROGRESS_LEN( tor ), file );
161    free( buf );
162
163    /* Write download and upload totals */
164    fastResumeWriteData( FR_ID_DOWNLOADED, &tor->downloaded, 8, 1, file );
165    fastResumeWriteData( FR_ID_UPLOADED, &tor->uploaded, 8, 1, file );
166
167    fclose( file );
168
169    tr_dbg( "Resume file '%s' written", path );
170    free( path );
171}
172
173static int fastResumeLoadProgress( tr_io_t * io, FILE * file )
174{
175    tr_torrent_t * tor = io->tor;
176    tr_info_t    * inf = &tor->info;
177
178    int     * fileMTimes;
179    int       i, j;
180    uint8_t * buf;
181    size_t    len;
182
183    len = FR_PROGRESS_LEN( tor );
184    buf = calloc( len, 1 );
185    if( len != fread( buf, 1, len, file ) )
186    {
187        tr_inf( "Could not read from resume file" );
188        free( buf );
189        return 1;
190    }
191
192    /* Compare file mtimes */
193    fileMTimes = malloc( FR_MTIME_LEN( tor ) );
194    if( fastResumeMTimes( io, fileMTimes ) )
195    {
196        free( buf );
197        free( fileMTimes );
198        return 1;
199    }
200    if( memcmp( fileMTimes, buf, FR_MTIME_LEN( tor ) ) )
201    {
202        tr_inf( "File mtimes don't match" );
203        free( buf );
204        free( fileMTimes );
205        return 1;
206    }
207    free( fileMTimes );
208
209    /* Copy the bitfield for blocks and fill blockHave */
210    tr_cpBlockBitfieldSet( tor->completion, buf + FR_MTIME_LEN( tor ) );
211
212    /* Copy the 'slotPiece' table */
213    memcpy( io->slotPiece, buf + FR_MTIME_LEN( tor ) +
214            FR_BLOCK_BITFIELD_LEN( tor ), FR_SLOTPIECE_LEN( tor ) );
215
216    free( buf );
217
218    /* Update io->pieceSlot, io->slotsUsed, and tor->bitfield */
219    io->slotsUsed = 0;
220    for( i = 0; i < inf->pieceCount; i++ )
221    {
222        io->pieceSlot[i] = -1;
223        for( j = 0; j < inf->pieceCount; j++ )
224        {
225            if( io->slotPiece[j] == i )
226            {
227                // tr_dbg( "Has piece %d in slot %d", i, j );
228                io->pieceSlot[i] = j;
229                io->slotsUsed = MAX( io->slotsUsed, j + 1 );
230                break;
231            }
232        }
233    }
234    // tr_dbg( "Slot used: %d", io->slotsUsed );
235
236    return 0;
237}
238
239static int fastResumeLoadOld( tr_io_t * io, FILE * file )
240{
241    tr_torrent_t * tor = io->tor;
242   
243    int size;
244
245    /* Check the size */
246    size = 4 + FR_PROGRESS_LEN( tor );
247    fseek( file, 0, SEEK_END );
248    if( ftell( file ) != size )
249    {
250        tr_inf( "Wrong size for resume file (%d bytes, %d expected)",
251                ftell( file ), size );
252        fclose( file );
253        return 1;
254    }
255
256    /* load progress information */
257    fseek( file, 4, SEEK_SET );
258    if( fastResumeLoadProgress( io, file ) )
259    {
260        fclose( file );
261        return 1;
262    }
263
264    fclose( file );
265
266    tr_inf( "Fast resuming successful (version 0)" );
267   
268    return 0;
269}
270
271static int fastResumeLoad( tr_io_t * io )
272{
273    tr_torrent_t * tor = io->tor;
274   
275    FILE    * file;
276    int       version = 0;
277    char    * path;
278    uint8_t   id;
279    uint32_t  len;
280    int       ret;
281
282    /* Open resume file */
283    path = fastResumeFileName( io );
284    if( !( file = fopen( path, "r" ) ) )
285    {
286        tr_inf( "Could not open '%s' for reading", path );
287        free( path );
288        return 1;
289    }
290    tr_dbg( "Resume file '%s' loaded", path );
291    free( path );
292
293    /* Check format version */
294    fread( &version, 4, 1, file );
295    if( 0 == version )
296    {
297        return fastResumeLoadOld( io, file );
298    }
299    if( 1 != version )
300    {
301        tr_inf( "Resume file has version %d, not supported", version );
302        fclose( file );
303        return 1;
304    }
305
306    ret = 1;
307    /* read each block of data */
308    while( 1 == fread( &id, 1, 1, file ) && 1 == fread( &len, 4, 1, file ) )
309    {
310        switch( id )
311        {
312            case FR_ID_PROGRESS:
313                /* read progress data */
314                if( (uint32_t)FR_PROGRESS_LEN( tor ) == len )
315                {
316                    if( fastResumeLoadProgress( io, file ) )
317                    {
318                        if( feof( file ) || ferror( file ) )
319                        {
320                            fclose( file );
321                            return 1;
322                        }
323                    }
324                    else
325                    {
326                        ret = 0;
327                    }
328                    continue;
329                }
330                break;
331
332            case FR_ID_DOWNLOADED:
333                /* read download total */
334                if( 8 == len)
335                {
336                    if( 1 != fread( &tor->downloaded, 8, 1, file ) )
337                    {
338                        fclose( file );
339                        return 1;
340                    }
341                    continue;
342                }
343                break;
344
345            case FR_ID_UPLOADED:
346                /* read upload total */
347                if( 8 == len)
348                {
349                    if( 1 != fread( &tor->uploaded, 8, 1, file ) )
350                    {
351                        fclose( file );
352                        return 1;
353                    }
354                    continue;
355                }
356                break;
357
358            default:
359                break;
360        }
361
362        /* if we didn't read the data, seek past it */
363        tr_inf( "Skipping resume data type %02x, %u bytes", id, len );
364        fseek( file, len, SEEK_CUR );
365    }
366
367    fclose( file );
368
369    if( !ret )
370    {
371        tr_inf( "Fast resuming successful" );
372    }
373   
374    return ret;
375}
Note: See TracBrowser for help on using the repository browser.