source: trunk/libtransmission/inout.c @ 1419

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

Reorganizes a few things, don't accept two connections from the same IP

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