source: branches/0.7x/libtransmission/inout.c @ 1787

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

Backport [1763]

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