source: trunk/libtransmission/fastresume.h @ 1

Last change on this file since 1 was 1, checked in by root, 15 years ago

Import from 2005-10-26

File size: 8.1 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 resume file is located in ~/.transmission/. Its name is
34 * "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 mtimes will be wrong,
39 * so the files will be scanned).
40 **********************************************************************/
41
42static char * fastResumeFolderName()
43{
44    char * ret;
45    asprintf( &ret, "%s/.transmission", getenv( "HOME" ) );
46    return ret;
47}
48
49static char * fastResumeFileName( tr_io_t * io )
50{
51    char * ret, * p;
52    int i;
53
54    p = fastResumeFolderName();
55    asprintf( &ret, "%s/resume.%40d", p, 0 );
56    free( p );
57
58    p = &ret[ strlen( ret ) - 2 * SHA_DIGEST_LENGTH ];
59    for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
60    {
61        sprintf( p, "%02x", io->tor->info.hash[i] );
62        p += 2;
63    }
64
65    return ret;
66}
67
68static int fastResumeMTimes( tr_io_t * io, int * tab )
69{
70    tr_torrent_t * tor = io->tor;
71    tr_info_t    * inf = &tor->info;
72
73    int           i;
74    char        * path;
75    struct stat   sb;
76
77    for( i = 0; i < inf->fileCount; i++ )
78    {
79        asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
80        if( stat( path, &sb ) )
81        {
82            tr_err( "Could not stat '%s'", path );
83            free( path );
84            return 1;
85        }
86        if( ( sb.st_mode & S_IFMT ) != S_IFREG )
87        {
88            tr_err( "Wrong st_mode for '%s'", path );
89            free( path );
90            return 1;
91        }
92        free( path );
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
101    return 0;
102}
103
104static void fastResumeSave( tr_io_t * io )
105{
106    tr_torrent_t * tor = io->tor;
107    tr_info_t    * inf = &tor->info;
108   
109    FILE    * file;
110    int       version = 0;
111    char    * path;
112    int     * fileMTimes;
113    int       i;
114    uint8_t * blockBitfield;
115
116    /* Get file sizes */
117    fileMTimes = malloc( inf->fileCount * 4 );
118    if( fastResumeMTimes( io, fileMTimes ) )
119    {
120        free( fileMTimes );
121        return;
122    }
123
124    /* Create folder if missing */
125    path = fastResumeFolderName();
126    mkdir( path, 0755 );
127    free( path );
128
129    /* Create/overwrite the resume file */
130    path = fastResumeFileName( io );
131    if( !( file = fopen( path, "w" ) ) )
132    {
133        tr_err( "Could not open '%s' for writing", path );
134        free( fileMTimes );
135        free( path );
136        return;
137    }
138    free( path );
139   
140    /* Write format version */
141    fwrite( &version, 4, 1, file );
142
143    /* Write file mtimes */
144    fwrite( fileMTimes, 4, inf->fileCount, file );
145    free( fileMTimes );
146
147    /* Build and write the bitfield for blocks */
148    blockBitfield = calloc( ( tor->blockCount + 7 ) / 8, 1 );
149    for( i = 0; i < tor->blockCount; i++ )
150    {
151        if( tor->blockHave[i] < 0 )
152        {
153            tr_bitfieldAdd( blockBitfield, i );
154        }
155    }
156    fwrite( blockBitfield, ( tor->blockCount + 7 ) / 8, 1, file );
157    free( blockBitfield );
158
159    /* Write the 'slotPiece' table */
160    fwrite( io->slotPiece, 4, inf->pieceCount, file );
161
162    fclose( file );
163}
164
165static int fastResumeLoad( tr_io_t * io )
166{
167    tr_torrent_t * tor = io->tor;
168    tr_info_t    * inf = &tor->info;
169   
170    FILE    * file;
171    int       version = 0;
172    char    * path;
173    int     * fileMTimes1, * fileMTimes2;
174    int       i, j;
175    uint8_t * blockBitfield;
176
177    int size;
178
179    /* Open resume file */
180    path = fastResumeFileName( io );
181    if( !( file = fopen( path, "r" ) ) )
182    {
183        tr_inf( "Could not open '%s' for reading", path );
184        free( path );
185        return 1;
186    }
187    free( path );
188
189    /* Check the size */
190    size = 4 + 4 * inf->fileCount + 4 * inf->pieceCount +
191        ( tor->blockCount + 7 ) / 8;
192    fseek( file, 0, SEEK_END );
193    if( ftell( file ) != size )
194    {
195        tr_inf( "Wrong size for resume file (%d bytes, %d expected)",
196                ftell( file ), size );
197        fclose( file );
198        return 1;
199    }
200    fseek( file, 0, SEEK_SET );
201
202    /* Check format version */
203    fread( &version, 4, 1, file );
204    if( version != 0 )
205    {
206        tr_inf( "Resume file has version %d, not supported",
207                version );
208        fclose( file );
209        return 1;
210    }
211
212    /* Compare file mtimes */
213    fileMTimes1 = malloc( inf->fileCount * 4 );
214    if( fastResumeMTimes( io, fileMTimes1 ) )
215    {
216        free( fileMTimes1 );
217        fclose( file );
218        return 1;
219    }
220    fileMTimes2 = malloc( inf->fileCount * 4 );
221    fread( fileMTimes2, 4, inf->fileCount, file );
222    if( memcmp( fileMTimes1, fileMTimes2, inf->fileCount * 4 ) )
223    {
224        tr_inf( "File mtimes don't match" );
225        free( fileMTimes1 );
226        free( fileMTimes2 );
227        fclose( file );
228        return 1;
229    }
230    free( fileMTimes1 );
231    free( fileMTimes2 );
232
233    /* Load the bitfield for blocks and fill blockHave */
234    blockBitfield = calloc( ( tor->blockCount + 7 ) / 8, 1 );
235    fread( blockBitfield, ( tor->blockCount + 7 ) / 8, 1, file );
236    tor->blockHaveCount = 0;
237    for( i = 0; i < tor->blockCount; i++ )
238    {
239        if( tr_bitfieldHas( blockBitfield, i ) )
240        {
241            tor->blockHave[i] = -1;
242            (tor->blockHaveCount)++;
243        }
244    }
245    free( blockBitfield );
246
247    /* Load the 'slotPiece' table */
248    fread( io->slotPiece, 4, inf->pieceCount, file );
249
250    fclose( file );
251
252    /* Update io->pieceSlot, io->slotsUsed, and tor->bitfield */
253    io->slotsUsed = 0;
254    for( i = 0; i < inf->pieceCount; i++ )
255    {
256        io->pieceSlot[i] = -1;
257        for( j = 0; j < inf->pieceCount; j++ )
258        {
259            if( io->slotPiece[j] == i )
260            {
261                // tr_dbg( "Has piece %d in slot %d", i, j );
262                io->pieceSlot[i] = j;
263                io->slotsUsed = MAX( io->slotsUsed, j + 1 );
264                break;
265            }
266        }
267
268        for( j = tr_pieceStartBlock( i );
269             j < tr_pieceStartBlock( i ) + tr_pieceCountBlocks( i );
270             j++ )
271        {
272            if( tor->blockHave[j] > -1 )
273            {
274                break;
275            }
276        }
277        if( j >= tr_pieceStartBlock( i ) + tr_pieceCountBlocks( i ) )
278        {
279            // tr_dbg( "Piece %d is complete", i );
280            tr_bitfieldAdd( tor->bitfield, i );
281        }
282    }
283    // tr_dbg( "Slot used: %d", io->slotsUsed );
284
285    tr_inf( "Fast resuming successful" );
286   
287    return 0;
288}
Note: See TracBrowser for help on using the repository browser.