source: trunk/libtransmission/inout.c @ 346

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

Fixes #22 (all torrents apparently at 0 % when T restarts)

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