source: trunk/libtransmission/fastresume.h @ 2

Last change on this file since 2 was 2, checked in by root, 16 years ago

Update 2005-11-01

File size: 7.9 KB
Line 
1/******************************************************************************
2 * Copyright (c) 2005 Eric Petit
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *****************************************************************************/
22
23/***********************************************************************
24 * Fast resume
25 ***********************************************************************
26 * Format of the resume file:
27 *  - 4 bytes: format version (currently 0)
28 *  - 4 bytes * number of files: mtimes of files
29 *  - 1 bit * number of blocks: whether we have the block or not
30 *  - 4 bytes * number of pieces (byte aligned): the pieces that have
31 *    been completed or started in each slot
32 *
33 * The name of the resume file is "resume.<hash>".
34 *
35 * All values are stored in the native endianness. Moving a
36 * libtransmission resume file from an architecture to another will not
37 * work, although it will not hurt either (the mtimes will be wrong,
38 * so the files will be scanned).
39 **********************************************************************/
40
41static char * fastResumeFileName( tr_io_t * io )
42{
43    tr_torrent_t * tor = io->tor;
44    char * ret, * p;
45    int i;
46
47    asprintf( &ret, "%s/resume.%40d", tor->prefsDirectory, 0 );
48
49    p = &ret[ strlen( ret ) - 2 * SHA_DIGEST_LENGTH ];
50    for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
51    {
52        sprintf( p, "%02x", io->tor->info.hash[i] );
53        p += 2;
54    }
55
56    return ret;
57}
58
59static int fastResumeMTimes( tr_io_t * io, int * tab )
60{
61    tr_torrent_t * tor = io->tor;
62    tr_info_t    * inf = &tor->info;
63
64    int           i;
65    char        * path;
66    struct stat   sb;
67
68    for( i = 0; i < inf->fileCount; i++ )
69    {
70        asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
71        if( stat( path, &sb ) )
72        {
73            tr_err( "Could not stat '%s'", path );
74            free( path );
75            return 1;
76        }
77        if( ( sb.st_mode & S_IFMT ) != S_IFREG )
78        {
79            tr_err( "Wrong st_mode for '%s'", path );
80            free( path );
81            return 1;
82        }
83        free( path );
84
85#ifdef SYS_DARWIN
86        tab[i] = ( sb.st_mtimespec.tv_sec & 0x7FFFFFFF );
87#else
88        tab[i] = ( sb.st_mtime & 0x7FFFFFFF );
89#endif
90    }
91
92    return 0;
93}
94
95static void fastResumeSave( tr_io_t * io )
96{
97    tr_torrent_t * tor = io->tor;
98    tr_info_t    * inf = &tor->info;
99   
100    FILE    * file;
101    int       version = 0;
102    char    * path;
103    int     * fileMTimes;
104    int       i;
105    uint8_t * blockBitfield;
106
107    /* Get file sizes */
108    fileMTimes = malloc( inf->fileCount * 4 );
109    if( fastResumeMTimes( io, fileMTimes ) )
110    {
111        free( fileMTimes );
112        return;
113    }
114
115    /* Create/overwrite the resume file */
116    path = fastResumeFileName( io );
117    if( !( file = fopen( path, "w" ) ) )
118    {
119        tr_err( "Could not open '%s' for writing", path );
120        free( fileMTimes );
121        free( path );
122        return;
123    }
124   
125    /* Write format version */
126    fwrite( &version, 4, 1, file );
127
128    /* Write file mtimes */
129    fwrite( fileMTimes, 4, inf->fileCount, file );
130    free( fileMTimes );
131
132    /* Build and write the bitfield for blocks */
133    blockBitfield = calloc( ( tor->blockCount + 7 ) / 8, 1 );
134    for( i = 0; i < tor->blockCount; i++ )
135    {
136        if( tor->blockHave[i] < 0 )
137        {
138            tr_bitfieldAdd( blockBitfield, i );
139        }
140    }
141    fwrite( blockBitfield, ( tor->blockCount + 7 ) / 8, 1, file );
142    free( blockBitfield );
143
144    /* Write the 'slotPiece' table */
145    fwrite( io->slotPiece, 4, inf->pieceCount, file );
146
147    fclose( file );
148
149    tr_dbg( "Resume file '%s' written", path );
150    free( path );
151}
152
153static int fastResumeLoad( tr_io_t * io )
154{
155    tr_torrent_t * tor = io->tor;
156    tr_info_t    * inf = &tor->info;
157   
158    FILE    * file;
159    int       version = 0;
160    char    * path;
161    int     * fileMTimes1, * fileMTimes2;
162    int       i, j;
163    uint8_t * blockBitfield;
164
165    int size;
166
167    /* Open resume file */
168    path = fastResumeFileName( io );
169    if( !( file = fopen( path, "r" ) ) )
170    {
171        tr_inf( "Could not open '%s' for reading", path );
172        free( path );
173        return 1;
174    }
175    tr_dbg( "Resume file '%s' loaded", path );
176    free( path );
177
178    /* Check the size */
179    size = 4 + 4 * inf->fileCount + 4 * inf->pieceCount +
180        ( tor->blockCount + 7 ) / 8;
181    fseek( file, 0, SEEK_END );
182    if( ftell( file ) != size )
183    {
184        tr_inf( "Wrong size for resume file (%d bytes, %d expected)",
185                ftell( file ), size );
186        fclose( file );
187        return 1;
188    }
189    fseek( file, 0, SEEK_SET );
190
191    /* Check format version */
192    fread( &version, 4, 1, file );
193    if( version != 0 )
194    {
195        tr_inf( "Resume file has version %d, not supported",
196                version );
197        fclose( file );
198        return 1;
199    }
200
201    /* Compare file mtimes */
202    fileMTimes1 = malloc( inf->fileCount * 4 );
203    if( fastResumeMTimes( io, fileMTimes1 ) )
204    {
205        free( fileMTimes1 );
206        fclose( file );
207        return 1;
208    }
209    fileMTimes2 = malloc( inf->fileCount * 4 );
210    fread( fileMTimes2, 4, inf->fileCount, file );
211    if( memcmp( fileMTimes1, fileMTimes2, inf->fileCount * 4 ) )
212    {
213        tr_inf( "File mtimes don't match" );
214        free( fileMTimes1 );
215        free( fileMTimes2 );
216        fclose( file );
217        return 1;
218    }
219    free( fileMTimes1 );
220    free( fileMTimes2 );
221
222    /* Load the bitfield for blocks and fill blockHave */
223    blockBitfield = calloc( ( tor->blockCount + 7 ) / 8, 1 );
224    fread( blockBitfield, ( tor->blockCount + 7 ) / 8, 1, file );
225    tor->blockHaveCount = 0;
226    for( i = 0; i < tor->blockCount; i++ )
227    {
228        if( tr_bitfieldHas( blockBitfield, i ) )
229        {
230            tor->blockHave[i] = -1;
231            (tor->blockHaveCount)++;
232        }
233    }
234    free( blockBitfield );
235
236    /* Load the 'slotPiece' table */
237    fread( io->slotPiece, 4, inf->pieceCount, file );
238
239    fclose( file );
240
241    /* Update io->pieceSlot, io->slotsUsed, and tor->bitfield */
242    io->slotsUsed = 0;
243    for( i = 0; i < inf->pieceCount; i++ )
244    {
245        io->pieceSlot[i] = -1;
246        for( j = 0; j < inf->pieceCount; j++ )
247        {
248            if( io->slotPiece[j] == i )
249            {
250                // tr_dbg( "Has piece %d in slot %d", i, j );
251                io->pieceSlot[i] = j;
252                io->slotsUsed = MAX( io->slotsUsed, j + 1 );
253                break;
254            }
255        }
256
257        for( j = tr_pieceStartBlock( i );
258             j < tr_pieceStartBlock( i ) + tr_pieceCountBlocks( i );
259             j++ )
260        {
261            if( tor->blockHave[j] > -1 )
262            {
263                break;
264            }
265        }
266        if( j >= tr_pieceStartBlock( i ) + tr_pieceCountBlocks( i ) )
267        {
268            // tr_dbg( "Piece %d is complete", i );
269            tr_bitfieldAdd( tor->bitfield, i );
270        }
271    }
272    // tr_dbg( "Slot used: %d", io->slotsUsed );
273
274    tr_inf( "Fast resuming successful" );
275   
276    return 0;
277}
Note: See TracBrowser for help on using the repository browser.