source: trunk/libtransmission/inout.c @ 1420

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

Officially give up on making libT reentrant, and simplify our code instead

  • Property svn:keywords set to Date Rev Author Id
File size: 15.4 KB
Line 
1/******************************************************************************
2 * $Id: inout.c 1420 2007-01-21 07:16:18Z 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
226    io->pieceSlot = malloc( inf->pieceCount * sizeof( int ) );
227    io->slotPiece = malloc( inf->pieceCount * sizeof( int ) );
228
229    if( !fastResumeLoad( io ) )
230    {
231        return 0;
232    }
233
234    tr_dbg( "Checking pieces..." );
235
236    /* Yet we don't have anything */
237    memset( io->pieceSlot, 0xFF, inf->pieceCount * sizeof( int ) );
238    memset( io->slotPiece, 0xFF, inf->pieceCount * sizeof( int ) );
239
240    /* Check pieces */
241    io->slotsUsed = 0;
242    buf           = malloc( inf->pieceSize );
243    for( i = 0; i < inf->pieceCount; i++ )
244    {
245        int size, j;
246
247        if( readSlot( io, i, buf, &size ) )
248        {
249            break;
250        }
251
252        io->slotsUsed = i + 1;
253        SHA1( buf, size, hash );
254
255        for( j = i; j < inf->pieceCount - 1; j++ )
256        {
257            if( !memcmp( hash, &inf->pieces[20*j], SHA_DIGEST_LENGTH ) )
258            {
259                io->pieceSlot[j] = i;
260                io->slotPiece[i] = j;
261
262                tr_cpPieceAdd( tor->completion, j );
263                break;
264            }
265        }
266
267        if( io->slotPiece[i] > -1 )
268        {
269            continue;
270        }
271
272        /* Special case for the last piece */
273        SHA1( buf, tr_pieceSize( inf->pieceCount - 1 ), hash );
274        if( !memcmp( hash, &inf->pieces[20 * (inf->pieceCount - 1)],
275                     SHA_DIGEST_LENGTH ) )
276        {
277            io->pieceSlot[inf->pieceCount - 1] = i;
278            io->slotPiece[i]                   = inf->pieceCount - 1;
279
280            tr_cpPieceAdd( tor->completion, inf->pieceCount - 1 );
281        }
282    }
283    free( buf );
284
285    return 0;
286}
287
288/***********************************************************************
289 * closeFiles
290 **********************************************************************/
291static void closeFiles( tr_io_t * io )
292{
293    tr_torrent_t * tor = io->tor;
294    tr_info_t    * inf = &tor->info;
295
296    int i;
297
298    for( i = 0; i < inf->fileCount; i++ )
299    {
300        tr_fdFileClose( tor->destination, inf->files[i].name );
301    }
302}
303
304/***********************************************************************
305 * readOrWriteBytes
306 ***********************************************************************
307 *
308 **********************************************************************/
309typedef size_t (* iofunc) ( int, void *, size_t );
310static int readOrWriteBytes( tr_io_t * io, uint64_t offset, int size,
311                             uint8_t * buf, int isWrite )
312{
313    tr_torrent_t * tor = io->tor;
314    tr_info_t    * inf = &tor->info;
315
316    int    piece = offset / inf->pieceSize;
317    int    begin = offset % inf->pieceSize;
318    int    i;
319    size_t cur;
320    int    file;
321    iofunc readOrWrite = isWrite ? (iofunc) write : (iofunc) read;
322    int    ret = 0;
323
324    /* Release the torrent lock so the UI can still update itself if
325       this blocks for a while */
326    tr_lockUnlock( &tor->lock );
327
328    /* We don't ever read or write more than a piece at a time */
329    if( tr_pieceSize( piece ) < begin + size )
330    {
331        tr_err( "readOrWriteBytes: trying to write more than a piece" );
332        ret = TR_ERROR_ASSERT;
333        goto cleanup;
334    }
335
336    /* Find which file we shall start reading/writing in */
337    for( i = 0; i < inf->fileCount; i++ )
338    {
339        if( offset < inf->files[i].length )
340        {
341            /* This is the file */
342            break;
343        }
344        offset -= inf->files[i].length;
345    }
346    if( i >= inf->fileCount )
347    {
348        /* Should not happen */
349        tr_err( "readOrWriteBytes: offset out of range (%"PRIu64", %d, %d)",
350                offset, size, isWrite );
351        ret = TR_ERROR_ASSERT;
352        goto cleanup;
353    }
354
355    while( size > 0 )
356    {
357        /* How much can we put or take with this file */
358        if( inf->files[i].length < offset + size )
359        {
360            cur = (int) ( inf->files[i].length - offset );
361        }
362        else
363        {
364            cur = size;
365        }
366
367        if( cur > 0 )
368        {
369            /* Now let's get a descriptor on the file... */
370            file = tr_fdFileOpen( tor->destination, inf->files[i].name,
371                                  isWrite );
372            if( file < 0 )
373            {
374                ret = file;
375                goto cleanup;
376            }
377
378            /* seek to the right offset... */
379            if( lseek( file, offset, SEEK_SET ) < 0 )
380            {
381                tr_fdFileRelease( file );
382                ret = TR_ERROR_IO_OTHER;
383                goto cleanup;
384            }
385
386            /* do what we are here to do... */
387            if( readOrWrite( file, buf, cur ) != cur )
388            {
389                tr_fdFileRelease( file );
390                ret = TR_ERROR_IO_OTHER;
391                goto cleanup;
392            }
393
394            /* and release the descriptor. */
395            tr_fdFileRelease( file );
396        }
397
398        /* 'cur' bytes done, 'size - cur' bytes to go with the next file */
399        i      += 1;
400        offset  = 0;
401        size   -= cur;
402        buf    += cur;
403    }
404
405cleanup:
406    tr_lockLock( &tor->lock );
407    return ret;
408}
409
410/***********************************************************************
411 * readSlot
412 ***********************************************************************
413 *
414 **********************************************************************/
415static int readOrWriteSlot( tr_io_t * io, int slot, uint8_t * buf,
416                            int * size, int write )
417{
418    tr_torrent_t * tor = io->tor;
419    tr_info_t    * inf = &tor->info;
420
421    uint64_t offset = (uint64_t) slot * (uint64_t) inf->pieceSize;
422   
423    *size = 0;
424    if( slot == inf->pieceCount - 1 )
425    {
426        *size = inf->totalSize % inf->pieceSize;
427    }
428    if( !*size )
429    {
430        *size = inf->pieceSize;
431    }
432
433    return readOrWriteBytes( io, offset, *size, buf, write );
434}
435
436static void invertSlots( tr_io_t * io, int slot1, int slot2 )
437{
438    tr_torrent_t * tor = io->tor;
439    tr_info_t    * inf = &tor->info;
440
441    uint8_t * buf1, * buf2;
442    int piece1, piece2, foo;
443
444    buf1 = calloc( inf->pieceSize, 1 );
445    buf2 = calloc( inf->pieceSize, 1 );
446
447    readSlot( io, slot1, buf1, &foo );
448    readSlot( io, slot2, buf2, &foo );
449
450    writeSlot( io, slot1, buf2, &foo );
451    writeSlot( io, slot2, buf1, &foo );
452
453    free( buf1 );
454    free( buf2 );
455
456    piece1                = io->slotPiece[slot1];
457    piece2                = io->slotPiece[slot2];
458    io->slotPiece[slot1]  = piece2;
459    io->slotPiece[slot2]  = piece1;
460    if( piece1 >= 0 )
461    {
462        io->pieceSlot[piece1] = slot2;
463    }
464    if( piece2 >= 0 )
465    {
466        io->pieceSlot[piece2] = slot1;
467    }
468}
469
470static void reorderPieces( tr_io_t * io )
471{
472    tr_torrent_t * tor = io->tor;
473    tr_info_t    * inf = &tor->info;
474
475    int i, didInvert;
476
477    /* Try to move pieces to their final places */
478    do
479    {
480        didInvert = 0;
481
482        for( i = 0; i < inf->pieceCount; i++ )
483        {
484            if( io->pieceSlot[i] < 0 )
485            {
486                /* We haven't started this piece yet */
487                continue;
488            }
489            if( io->pieceSlot[i] == i )
490            {
491                /* Already in place */
492                continue;
493            }
494            if( i >= io->slotsUsed )
495            {
496                /* File is not big enough yet */
497                continue;
498            }
499
500            /* Move piece i into slot i */
501            tr_inf( "invert %d and %d", io->pieceSlot[i], i );
502            invertSlots( io, i, io->pieceSlot[i] );
503            didInvert = 1;
504        }
505    } while( didInvert );
506}
507
508static void findSlotForPiece( tr_io_t * io, int piece )
509{
510    int i;
511#if 0
512    tr_torrent_t * tor = io->tor;
513    tr_info_t    * inf = &tor->info;
514
515    tr_dbg( "Entering findSlotForPiece" );
516
517    for( i = 0; i < inf->pieceCount; i++ )
518        printf( "%02d ", io->slotPiece[i] );
519    printf( "\n" );
520    for( i = 0; i < inf->pieceCount; i++ )
521        printf( "%02d ", io->pieceSlot[i] );
522    printf( "\n" );
523#endif
524
525    /* Look for an empty slot somewhere */
526    for( i = 0; i < io->slotsUsed; i++ )
527    {
528        if( io->slotPiece[i] < 0 )
529        {
530            io->pieceSlot[piece] = i;
531            io->slotPiece[i]     = piece;
532            goto reorder;
533        }
534    }
535
536    /* No empty slot, extend the file */
537    io->pieceSlot[piece]         = io->slotsUsed;
538    io->slotPiece[io->slotsUsed] = piece;
539    (io->slotsUsed)++;
540
541  reorder:
542    reorderPieces( io );
543
544#if 0
545    for( i = 0; i < inf->pieceCount; i++ )
546        printf( "%02d ", io->slotPiece[i] );
547    printf( "\n" );
548    for( i = 0; i < inf->pieceCount; i++ )
549        printf( "%02d ", io->pieceSlot[i] );
550    printf( "\n" );
551
552    printf( "Leaving findSlotForPiece\n" );
553#endif
554}
Note: See TracBrowser for help on using the repository browser.