source: trunk/libtransmission/fastresume.h @ 3

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

Update 2005-11-17

File size: 7.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 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    uint8_t * blockBitfield;
105
106    /* Get file sizes */
107    fileMTimes = malloc( inf->fileCount * 4 );
108    if( fastResumeMTimes( io, fileMTimes ) )
109    {
110        free( fileMTimes );
111        return;
112    }
113
114    /* Create/overwrite the resume file */
115    path = fastResumeFileName( io );
116    if( !( file = fopen( path, "w" ) ) )
117    {
118        tr_err( "Could not open '%s' for writing", path );
119        free( fileMTimes );
120        free( path );
121        return;
122    }
123   
124    /* Write format version */
125    fwrite( &version, 4, 1, file );
126
127    /* Write file mtimes */
128    fwrite( fileMTimes, 4, inf->fileCount, file );
129    free( fileMTimes );
130
131    /* Build and write the bitfield for blocks */
132    blockBitfield = tr_cpBlockBitfield( tor->completion );
133    fwrite( blockBitfield, ( tor->blockCount + 7 ) / 8, 1, file );
134
135    /* Write the 'slotPiece' table */
136    fwrite( io->slotPiece, 4, inf->pieceCount, file );
137
138    fclose( file );
139
140    tr_dbg( "Resume file '%s' written", path );
141    free( path );
142}
143
144static int fastResumeLoad( tr_io_t * io )
145{
146    tr_torrent_t * tor = io->tor;
147    tr_info_t    * inf = &tor->info;
148   
149    FILE    * file;
150    int       version = 0;
151    char    * path;
152    int     * fileMTimes1, * fileMTimes2;
153    int       i, j;
154    uint8_t * blockBitfield;
155
156    int size;
157
158    /* Open resume file */
159    path = fastResumeFileName( io );
160    if( !( file = fopen( path, "r" ) ) )
161    {
162        tr_inf( "Could not open '%s' for reading", path );
163        free( path );
164        return 1;
165    }
166    tr_dbg( "Resume file '%s' loaded", path );
167    free( path );
168
169    /* Check the size */
170    size = 4 + 4 * inf->fileCount + 4 * inf->pieceCount +
171        ( tor->blockCount + 7 ) / 8;
172    fseek( file, 0, SEEK_END );
173    if( ftell( file ) != size )
174    {
175        tr_inf( "Wrong size for resume file (%d bytes, %d expected)",
176                ftell( file ), size );
177        fclose( file );
178        return 1;
179    }
180    fseek( file, 0, SEEK_SET );
181
182    /* Check format version */
183    fread( &version, 4, 1, file );
184    if( version != 0 )
185    {
186        tr_inf( "Resume file has version %d, not supported",
187                version );
188        fclose( file );
189        return 1;
190    }
191
192    /* Compare file mtimes */
193    fileMTimes1 = malloc( inf->fileCount * 4 );
194    if( fastResumeMTimes( io, fileMTimes1 ) )
195    {
196        free( fileMTimes1 );
197        fclose( file );
198        return 1;
199    }
200    fileMTimes2 = malloc( inf->fileCount * 4 );
201    fread( fileMTimes2, 4, inf->fileCount, file );
202    if( memcmp( fileMTimes1, fileMTimes2, inf->fileCount * 4 ) )
203    {
204        tr_inf( "File mtimes don't match" );
205        free( fileMTimes1 );
206        free( fileMTimes2 );
207        fclose( file );
208        return 1;
209    }
210    free( fileMTimes1 );
211    free( fileMTimes2 );
212
213    /* Load the bitfield for blocks and fill blockHave */
214    blockBitfield = calloc( ( tor->blockCount + 7 ) / 8, 1 );
215    fread( blockBitfield, ( tor->blockCount + 7 ) / 8, 1, file );
216    tr_cpBlockBitfieldSet( tor->completion, blockBitfield );
217    free( blockBitfield );
218
219    /* Load the 'slotPiece' table */
220    fread( io->slotPiece, 4, inf->pieceCount, file );
221
222    fclose( file );
223
224    /* Update io->pieceSlot, io->slotsUsed, and tor->bitfield */
225    io->slotsUsed = 0;
226    for( i = 0; i < inf->pieceCount; i++ )
227    {
228        io->pieceSlot[i] = -1;
229        for( j = 0; j < inf->pieceCount; j++ )
230        {
231            if( io->slotPiece[j] == i )
232            {
233                // tr_dbg( "Has piece %d in slot %d", i, j );
234                io->pieceSlot[i] = j;
235                io->slotsUsed = MAX( io->slotsUsed, j + 1 );
236                break;
237            }
238        }
239    }
240    // tr_dbg( "Slot used: %d", io->slotsUsed );
241
242    tr_inf( "Fast resuming successful" );
243   
244    return 0;
245}
Note: See TracBrowser for help on using the repository browser.