source: trunk/libtransmission/inout.c @ 104

Last change on this file since 104 was 104, checked in by joshe, 16 years ago

Save resume information when a torrent finishes downloading.

File size: 16.4 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#include "transmission.h"
24
25#ifdef SYS_BEOS
26# define fseeko _fseek
27#endif
28
29struct tr_io_s
30{
31    tr_torrent_t * tor;
32
33    /* Position of pieces
34       -1 = we haven't started to download this piece yet
35        n = we have started or completed the piece in slot n */
36    int         * pieceSlot;
37
38    /* Pieces in slot
39       -1 = unused slot
40        n = piece n */
41    int         * slotPiece;
42
43    int           slotsUsed;
44};
45
46#include "fastresume.h"
47
48/***********************************************************************
49 * Local prototypes
50 **********************************************************************/
51static int  createFiles( tr_io_t * );
52static int  checkFiles( tr_io_t * );
53static void closeFiles( tr_io_t * );
54static int  readOrWriteBytes( tr_io_t *, uint64_t, int, uint8_t *, int );
55static int  readOrWriteSlot( tr_io_t * io, int slot, uint8_t * buf,
56                             int * size, int write );
57static void findSlotForPiece( tr_io_t *, int );
58
59#define readBytes(io,o,s,b)  readOrWriteBytes(io,o,s,b,0)
60#define writeBytes(io,o,s,b) readOrWriteBytes(io,o,s,b,1)
61
62#define readSlot(io,sl,b,s)  readOrWriteSlot(io,sl,b,s,0)
63#define writeSlot(io,sl,b,s) readOrWriteSlot(io,sl,b,s,1)
64
65/***********************************************************************
66 * tr_ioInit
67 ***********************************************************************
68 * Open all files we are going to write to
69 **********************************************************************/
70tr_io_t * tr_ioInit( tr_torrent_t * tor )
71{
72    tr_io_t * io;
73
74    io      = malloc( sizeof( tr_io_t ) );
75    io->tor = tor;
76
77    if( createFiles( io ) || checkFiles( io ) )
78    {
79        free( io );
80        return NULL;
81    }
82
83    return io;
84}
85
86/***********************************************************************
87 * tr_ioRead
88 ***********************************************************************
89 *
90 **********************************************************************/
91int tr_ioRead( tr_io_t * io, int index, int begin, int length,
92               uint8_t * buf )
93{
94    uint64_t    offset;
95    tr_info_t * inf = &io->tor->info;
96
97    offset = (uint64_t) io->pieceSlot[index] *
98        (uint64_t) inf->pieceSize + (uint64_t) begin;
99
100    return readBytes( io, offset, length, buf );
101}
102
103/***********************************************************************
104 * tr_ioWrite
105 ***********************************************************************
106 *
107 **********************************************************************/
108int tr_ioWrite( tr_io_t * io, int index, int begin, int length,
109                uint8_t * buf )
110{
111    tr_torrent_t * tor = io->tor;
112    tr_info_t    * inf = &io->tor->info;
113    uint64_t       offset;
114    int            i;
115    uint8_t        hash[SHA_DIGEST_LENGTH];
116    uint8_t      * pieceBuf;
117    int            pieceSize;
118    int            startBlock, endBlock;
119
120    if( io->pieceSlot[index] < 0 )
121    {
122        findSlotForPiece( io, index );
123        tr_inf( "Piece %d: starting in slot %d", index,
124                io->pieceSlot[index] );
125    }
126
127    offset = (uint64_t) io->pieceSlot[index] *
128        (uint64_t) inf->pieceSize + (uint64_t) begin;
129
130    if( writeBytes( io, offset, length, buf ) )
131    {
132        return 1;
133    }
134
135    startBlock = tr_pieceStartBlock( index );
136    endBlock   = startBlock + tr_pieceCountBlocks( index );
137    for( i = startBlock; i < endBlock; i++ )
138    {
139        if( !tr_cpBlockIsComplete( tor->completion, i ) )
140        {
141            /* The piece is not complete */
142            return 0;
143        }
144    }
145
146    /* The piece is complete, check the hash */
147    pieceSize = tr_pieceSize( index );
148    pieceBuf  = malloc( pieceSize );
149    readBytes( io, (uint64_t) io->pieceSlot[index] *
150               (uint64_t) inf->pieceSize, pieceSize, pieceBuf );
151    SHA1( pieceBuf, pieceSize, hash );
152    free( pieceBuf );
153
154    if( memcmp( hash, &inf->pieces[20*index], SHA_DIGEST_LENGTH ) )
155    {
156        tr_inf( "Piece %d (slot %d): hash FAILED", index,
157                io->pieceSlot[index] );
158
159        /* We will need to reload the whole piece */
160        for( i = startBlock; i < endBlock; i++ )
161        {
162            tr_cpBlockRem( tor->completion, i );
163        }
164    }
165    else
166    {
167        tr_inf( "Piece %d (slot %d): hash OK", index,
168                io->pieceSlot[index] );
169        tr_cpPieceAdd( tor->completion, index );
170    }
171
172    return 0;
173}
174
175void tr_ioClose( tr_io_t * io )
176{
177    closeFiles( io );
178
179    fastResumeSave( io );
180
181    free( io->pieceSlot );
182    free( io->slotPiece );
183    free( io );
184}
185
186void tr_ioSaveResume( tr_io_t * io )
187{
188    fastResumeSave( io );
189}
190
191/***********************************************************************
192 * createFiles
193 ***********************************************************************
194 * Make sure the existing folders/files have correct types and
195 * permissions, and create missing folders and files
196 **********************************************************************/
197static int createFiles( tr_io_t * io )
198{
199    tr_torrent_t * tor = io->tor;
200    tr_info_t    * inf = &tor->info;
201
202    int           i;
203    char        * path, * p;
204    struct stat   sb;
205    FILE        * file;
206
207    tr_dbg( "Creating files..." );
208
209    for( i = 0; i < inf->fileCount; i++ )
210    {
211        asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
212
213        /* Create folders */
214        p = path;
215        while( ( p = strchr( p, '/' ) ) )
216        {
217            *p = '\0';
218            if( stat( path, &sb ) )
219            {
220                /* Folder doesn't exist yet */
221                mkdir( path, 0755 );
222            }
223            else if( ( sb.st_mode & S_IFMT ) != S_IFDIR )
224            {
225                /* Node exists but isn't a folder */
226                printf( "Remove %s, it's in the way.\n", path );
227                free( path );
228                return 1;
229            }
230            *p = '/';
231            p++;
232        }
233
234        if( stat( path, &sb ) )
235        {
236            /* File doesn't exist yet */
237            if( !( file = fopen( path, "w" ) ) )
238            {
239                tr_err( "Could not create `%s' (%s)", path,
240                        strerror( errno ) );
241                free( path );
242                return 1;
243            }
244            fclose( file );
245        }
246        else if( ( sb.st_mode & S_IFMT ) != S_IFREG )
247        {
248            /* Node exists but isn't a file */
249            printf( "Remove %s, it's in the way.\n", path );
250            free( path );
251            return 1;
252        }
253
254        free( path );
255    }
256
257    return 0;
258}
259
260/***********************************************************************
261 * checkFiles
262 ***********************************************************************
263 * Look for pieces
264 **********************************************************************/
265static int checkFiles( tr_io_t * io )
266{
267    tr_torrent_t * tor = io->tor;
268    tr_info_t    * inf = &tor->info;
269
270    int i;
271    uint8_t * buf;
272    uint8_t hash[SHA_DIGEST_LENGTH];
273
274    io->pieceSlot = malloc( inf->pieceCount * sizeof( int ) );
275    io->slotPiece = malloc( inf->pieceCount * sizeof( int ) );
276
277    if( !fastResumeLoad( io ) )
278    {
279        return 0;
280    }
281
282    tr_dbg( "Checking pieces..." );
283
284    /* Yet we don't have anything */
285    memset( io->pieceSlot, 0xFF, inf->pieceCount * sizeof( int ) );
286    memset( io->slotPiece, 0xFF, inf->pieceCount * sizeof( int ) );
287
288    /* Check pieces */
289    io->slotsUsed = 0;
290    buf           = malloc( inf->pieceSize );
291    for( i = 0; i < inf->pieceCount; i++ )
292    {
293        int size, j;
294
295        if( readSlot( io, i, buf, &size ) )
296        {
297            break;
298        }
299
300        io->slotsUsed = i + 1;
301        SHA1( buf, size, hash );
302
303        for( j = i; j < inf->pieceCount - 1; j++ )
304        {
305            if( !memcmp( hash, &inf->pieces[20*j], SHA_DIGEST_LENGTH ) )
306            {
307                io->pieceSlot[j] = i;
308                io->slotPiece[i] = j;
309
310                tr_cpPieceAdd( tor->completion, j );
311                break;
312            }
313        }
314
315        if( io->slotPiece[i] > -1 )
316        {
317            continue;
318        }
319
320        /* Special case for the last piece */
321        SHA1( buf, tr_pieceSize( inf->pieceCount - 1 ), hash );
322        if( !memcmp( hash, &inf->pieces[20 * (inf->pieceCount - 1)],
323                     SHA_DIGEST_LENGTH ) )
324        {
325            io->pieceSlot[inf->pieceCount - 1] = i;
326            io->slotPiece[i]                   = inf->pieceCount - 1;
327
328            tr_cpPieceAdd( tor->completion, inf->pieceCount - 1 );
329        }
330    }
331    free( buf );
332
333    return 0;
334}
335
336/***********************************************************************
337 * closeFiles
338 **********************************************************************/
339static void closeFiles( tr_io_t * io )
340{
341    tr_torrent_t * tor = io->tor;
342    tr_info_t    * inf = &tor->info;
343
344    int i;
345    char * path;
346
347    for( i = 0; i < inf->fileCount; i++ )
348    {
349        asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
350        tr_fdFileClose( tor->fdlimit, path );
351        free( path );
352    }
353}
354
355/***********************************************************************
356 * readOrWriteBytes
357 ***********************************************************************
358 *
359 **********************************************************************/
360typedef size_t (* iofunc) ( void *, size_t, size_t, FILE * );
361static int readOrWriteBytes( tr_io_t * io, uint64_t offset, int size,
362                             uint8_t * buf, int write )
363{
364    tr_torrent_t * tor = io->tor;
365    tr_info_t    * inf = &tor->info;
366
367    int    piece = offset / inf->pieceSize;
368    int    begin = offset % inf->pieceSize;
369    int    i, cur;
370    char * path;
371    FILE * file;
372    iofunc readOrWrite = write ? (iofunc) fwrite : (iofunc) fread;
373
374    /* Release the torrent lock so the UI can still update itself if
375       this blocks for a while */
376    tr_lockUnlock( &tor->lock );
377
378    /* We don't ever read or write more than a piece at a time */
379    if( tr_pieceSize( piece ) < begin + size )
380    {
381        tr_err( "readOrWriteBytes: trying to write more than a piece" );
382        goto fail;
383    }
384
385    /* Find which file we shall start reading/writing in */
386    for( i = 0; i < inf->fileCount; i++ )
387    {
388        if( offset < inf->files[i].length )
389        {
390            /* This is the file */
391            break;
392        }
393        offset -= inf->files[i].length;
394    }
395    if( i >= inf->fileCount )
396    {
397        /* Should not happen */
398        tr_err( "readOrWriteBytes: offset out of range (%lld, %d, %d)",
399                offset, size, write );
400        goto fail;
401    }
402
403    while( size > 0 )
404    {
405        /* How much can we put or take with this file */
406        if( inf->files[i].length < offset + size )
407        {
408            cur = (int) ( inf->files[i].length - offset );
409        }
410        else
411        {
412            cur = size;
413        }
414
415        /* Now let's get a stream on the file... */
416        asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
417        file = tr_fdFileOpen( tor->fdlimit, path );
418        if( !file )
419        {
420            tr_err( "readOrWriteBytes: could not open file '%s'", path );
421            free( path );
422            goto fail;
423        }
424        free( path );
425
426        /* seek to the right offset... */
427        if( fseeko( file, offset, SEEK_SET ) )
428        {
429            goto fail;
430        }
431
432        /* do what we are here to do... */
433        if( readOrWrite( buf, cur, 1, file ) != 1 )
434        {
435            goto fail;
436        }
437
438        /* and close the stream. */
439        tr_fdFileRelease( tor->fdlimit, file );
440
441        /* 'cur' bytes done, 'size - cur' bytes to go with the next file */
442        i      += 1;
443        offset  = 0;
444        size   -= cur;
445        buf    += cur;
446    }
447
448    tr_lockLock( &tor->lock );
449    return 0;
450
451fail:
452    tr_lockLock( &tor->lock );
453    return 1;
454}
455
456/***********************************************************************
457 * readSlot
458 ***********************************************************************
459 *
460 **********************************************************************/
461static int readOrWriteSlot( tr_io_t * io, int slot, uint8_t * buf,
462                            int * size, int write )
463{
464    tr_torrent_t * tor = io->tor;
465    tr_info_t    * inf = &tor->info;
466
467    uint64_t offset = (uint64_t) slot * (uint64_t) inf->pieceSize;
468   
469    *size = 0;
470    if( slot == inf->pieceCount - 1 )
471    {
472        *size = inf->totalSize % inf->pieceSize;
473    }
474    if( !*size )
475    {
476        *size = inf->pieceSize;
477    }
478
479    return readOrWriteBytes( io, offset, *size, buf, write );
480}
481
482static void invertSlots( tr_io_t * io, int slot1, int slot2 )
483{
484    tr_torrent_t * tor = io->tor;
485    tr_info_t    * inf = &tor->info;
486
487    uint8_t * buf1, * buf2;
488    int piece1, piece2, foo;
489
490    buf1 = calloc( inf->pieceSize, 1 );
491    buf2 = calloc( inf->pieceSize, 1 );
492
493    readSlot( io, slot1, buf1, &foo );
494    readSlot( io, slot2, buf2, &foo );
495
496    writeSlot( io, slot1, buf2, &foo );
497    writeSlot( io, slot2, buf1, &foo );
498
499    free( buf1 );
500    free( buf2 );
501
502    piece1                = io->slotPiece[slot1];
503    piece2                = io->slotPiece[slot2];
504    io->slotPiece[slot1]  = piece2;
505    io->slotPiece[slot2]  = piece1;
506    if( piece1 >= 0 )
507    {
508        io->pieceSlot[piece1] = slot2;
509    }
510    if( piece2 >= 0 )
511    {
512        io->pieceSlot[piece2] = slot1;
513    }
514}
515
516static void reorderPieces( tr_io_t * io )
517{
518    tr_torrent_t * tor = io->tor;
519    tr_info_t    * inf = &tor->info;
520
521    int i, didInvert;
522
523    /* Try to move pieces to their final places */
524    do
525    {
526        didInvert = 0;
527
528        for( i = 0; i < inf->pieceCount; i++ )
529        {
530            if( io->pieceSlot[i] < 0 )
531            {
532                /* We haven't started this piece yet */
533                continue;
534            }
535            if( io->pieceSlot[i] == i )
536            {
537                /* Already in place */
538                continue;
539            }
540            if( i >= io->slotsUsed )
541            {
542                /* File is not big enough yet */
543                continue;
544            }
545
546            /* Move piece i into slot i */
547            tr_inf( "invert %d and %d", io->pieceSlot[i], i );
548            invertSlots( io, i, io->pieceSlot[i] );
549            didInvert = 1;
550        }
551    } while( didInvert );
552}
553
554static void findSlotForPiece( tr_io_t * io, int piece )
555{
556    int i;
557#if 0
558    tr_torrent_t * tor = io->tor;
559    tr_info_t    * inf = &tor->info;
560
561    tr_dbg( "Entering findSlotForPiece" );
562
563    for( i = 0; i < inf->pieceCount; i++ )
564        printf( "%02d ", io->slotPiece[i] );
565    printf( "\n" );
566    for( i = 0; i < inf->pieceCount; i++ )
567        printf( "%02d ", io->pieceSlot[i] );
568    printf( "\n" );
569#endif
570
571    /* Look for an empty slot somewhere */
572    for( i = 0; i < io->slotsUsed; i++ )
573    {
574        if( io->slotPiece[i] < 0 )
575        {
576            io->pieceSlot[piece] = i;
577            io->slotPiece[i]     = piece;
578            goto reorder;
579        }
580    }
581
582    /* No empty slot, extend the file */
583    io->pieceSlot[piece]         = io->slotsUsed;
584    io->slotPiece[io->slotsUsed] = piece;
585    (io->slotsUsed)++;
586
587  reorder:
588    reorderPieces( io );
589
590#if 0
591    for( i = 0; i < inf->pieceCount; i++ )
592        printf( "%02d ", io->slotPiece[i] );
593    printf( "\n" );
594    for( i = 0; i < inf->pieceCount; i++ )
595        printf( "%02d ", io->pieceSlot[i] );
596    printf( "\n" );
597
598    printf( "Leaving findSlotForPiece\n" );
599#endif
600}
Note: See TracBrowser for help on using the repository browser.