source: trunk/libtransmission/inout.c @ 1

Last change on this file since 1 was 1, checked in by root, 15 years ago

Import from 2005-10-26

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