source: trunk/libtransmission/inout.c @ 253

Last change on this file since 253 was 253, checked in by titer, 16 years ago

Adds automatic peer banning.
At first, peers get only banned for the bad pieces they've contributed to, i.e. we continue to ask them for other parts of the torrent. If more bad data keeps coming, the peer gets completely banned.
Based on Jeremiah Morris' patch.

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