source: trunk/libtransmission/inout.c @ 3

Last change on this file since 3 was 3, checked in by root, 16 years ago

Update 2005-11-17

File size: 15.9 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( !tr_cpBlockIsComplete( tor->completion, i ) )
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            tr_cpBlockRem( tor->completion, i );
163        }
164    }
165    else
166    {
167        tr_inf( "Piece %d (slot %d): hash OK", index,
168                io->pieceSlot[index] );
169        tr_cpPieceAdd( tor->completion, index );
170    }
171
172    return 0;
173}
174
175void tr_ioClose( tr_io_t * io )
176{
177    closeFiles( io );
178
179    fastResumeSave( io );
180
181    free( io->pieceSlot );
182    free( io->slotPiece );
183    free( io );
184}
185
186/***********************************************************************
187 * createFiles
188 ***********************************************************************
189 * Make sure the existing folders/files have correct types and
190 * permissions, and create missing folders and files
191 **********************************************************************/
192static int createFiles( tr_io_t * io )
193{
194    tr_torrent_t * tor = io->tor;
195    tr_info_t    * inf = &tor->info;
196
197    int           i;
198    char        * path, * p;
199    struct stat   sb;
200    FILE        * file;
201
202    tr_dbg( "Creating files..." );
203
204    for( i = 0; i < inf->fileCount; i++ )
205    {
206        asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
207
208        /* Create folders */
209        p = path;
210        while( ( p = strchr( p, '/' ) ) )
211        {
212            *p = '\0';
213            if( stat( path, &sb ) )
214            {
215                /* Folder doesn't exist yet */
216                mkdir( path, 0755 );
217            }
218            else if( ( sb.st_mode & S_IFMT ) != S_IFDIR )
219            {
220                /* Node exists but isn't a folder */
221                printf( "Remove %s, it's in the way.\n", path );
222                free( path );
223                return 1;
224            }
225            *p = '/';
226            p++;
227        }
228
229        if( stat( path, &sb ) )
230        {
231            /* File doesn't exist yet */
232            if( !( file = fopen( path, "w" ) ) )
233            {
234                tr_err( "Could not create `%s' (%s)", path,
235                        strerror( errno ) );
236                free( path );
237                return 1;
238            }
239            fclose( file );
240        }
241        else if( ( sb.st_mode & S_IFMT ) != S_IFREG )
242        {
243            /* Node exists but isn't a file */
244            printf( "Remove %s, it's in the way.\n", path );
245            free( path );
246            return 1;
247        }
248
249        free( path );
250    }
251
252    return 0;
253}
254
255/***********************************************************************
256 * checkFiles
257 ***********************************************************************
258 * Look for pieces
259 **********************************************************************/
260static int checkFiles( tr_io_t * io )
261{
262    tr_torrent_t * tor = io->tor;
263    tr_info_t    * inf = &tor->info;
264
265    int i;
266    uint8_t * buf;
267    uint8_t hash[SHA_DIGEST_LENGTH];
268
269    io->pieceSlot = malloc( inf->pieceCount * sizeof( int ) );
270    io->slotPiece = malloc( inf->pieceCount * sizeof( int ) );
271
272    if( !fastResumeLoad( io ) )
273    {
274        return 0;
275    }
276
277    tr_dbg( "Checking pieces..." );
278
279    /* Yet we don't have anything */
280    memset( io->pieceSlot, 0xFF, inf->pieceCount * sizeof( int ) );
281    memset( io->slotPiece, 0xFF, inf->pieceCount * sizeof( int ) );
282
283    /* Check pieces */
284    io->slotsUsed = 0;
285    buf           = malloc( inf->pieceSize );
286    for( i = 0; i < inf->pieceCount; i++ )
287    {
288        int size, j;
289
290        if( readSlot( io, i, buf, &size ) )
291        {
292            break;
293        }
294
295        io->slotsUsed = i + 1;
296        SHA1( buf, size, hash );
297
298        for( j = i; j < inf->pieceCount - 1; j++ )
299        {
300            if( !memcmp( hash, &inf->pieces[20*j], SHA_DIGEST_LENGTH ) )
301            {
302                io->pieceSlot[j] = i;
303                io->slotPiece[i] = j;
304
305                tr_cpPieceAdd( tor->completion, j );
306                break;
307            }
308        }
309
310        if( io->slotPiece[i] > -1 )
311        {
312            continue;
313        }
314
315        /* Special case for the last piece */
316        SHA1( buf, tr_pieceSize( inf->pieceCount - 1 ), hash );
317        if( !memcmp( hash, &inf->pieces[20 * (inf->pieceCount - 1)],
318                     SHA_DIGEST_LENGTH ) )
319        {
320            io->pieceSlot[inf->pieceCount - 1] = i;
321            io->slotPiece[i]                   = inf->pieceCount - 1;
322
323            tr_cpPieceAdd( tor->completion, inf->pieceCount - 1 );
324        }
325    }
326    free( buf );
327
328    return 0;
329}
330
331/***********************************************************************
332 * closeFiles
333 **********************************************************************/
334static void closeFiles( tr_io_t * io )
335{
336    tr_torrent_t * tor = io->tor;
337    tr_info_t    * inf = &tor->info;
338
339    int i;
340    char * path;
341
342    for( i = 0; i < inf->fileCount; i++ )
343    {
344        asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
345        tr_fdFileClose( tor->fdlimit, path );
346        free( path );
347    }
348}
349
350/***********************************************************************
351 * readOrWriteBytes
352 ***********************************************************************
353 *
354 **********************************************************************/
355static int readOrWriteBytes( tr_io_t * io, uint64_t offset, int size,
356                             uint8_t * buf, int write )
357{
358    tr_torrent_t * tor = io->tor;
359    tr_info_t    * inf = &tor->info;
360
361    int          piece = offset / inf->pieceSize;
362    int          begin = offset % inf->pieceSize;
363
364    int          i;
365    uint64_t     foo;
366    uint64_t     posInFile = 0;
367    int          willRead;
368    char       * path;
369    FILE       * file;
370
371    /* We can't ever read or write more than a piece at a time */
372    if( tr_pieceSize( piece ) < begin + size )
373    {
374        return 1;
375    }
376
377    /* Find which file we shall start reading/writing in */
378    foo = 0;
379    for( i = 0; i < inf->fileCount; i++ )
380    {
381        if( offset < foo + inf->files[i].length )
382        {
383            posInFile = offset - foo;
384            break;
385        }
386        foo += inf->files[i].length;
387    }
388
389    while( size > 0 )
390    {
391        asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
392        tr_lockUnlock( tor->lock );
393        file = tr_fdFileOpen( tor->fdlimit, path );
394        tr_lockLock( tor->lock );
395        free( path );
396
397        if( !file )
398        {
399            return 1;
400        }
401
402        willRead = MIN( inf->files[i].length - posInFile,
403                          (uint64_t) size );
404
405        tr_lockUnlock( tor->lock );
406        if( fseeko( file, posInFile, SEEK_SET ) )
407        {
408            tr_lockLock( tor->lock );
409            return 1;
410        }
411        if( write )
412        {
413            if( fwrite( buf, willRead, 1, file ) != 1 )
414            {
415                tr_lockLock( tor->lock );
416                return 1;
417            }
418        }
419        else
420        {
421            if( fread( buf, willRead, 1, file ) != 1 )
422            {
423                tr_lockLock( tor->lock );
424                return 1;
425            }
426        }
427        tr_lockLock( tor->lock );
428        tr_fdFileRelease( tor->fdlimit, file );
429
430        /* 'willRead' less bytes to do */
431        size -= willRead;
432        buf  += willRead;
433
434        /* Go to the beginning of the next file */
435        i         += 1;
436        posInFile  = 0;
437    }
438
439    return 0;
440}
441
442/***********************************************************************
443 * readSlot
444 ***********************************************************************
445 *
446 **********************************************************************/
447static int readOrWriteSlot( tr_io_t * io, int slot, uint8_t * buf,
448                            int * size, int write )
449{
450    tr_torrent_t * tor = io->tor;
451    tr_info_t    * inf = &tor->info;
452
453    uint64_t offset = (uint64_t) slot * (uint64_t) inf->pieceSize;
454   
455    *size = 0;
456    if( slot == inf->pieceCount - 1 )
457    {
458        *size = inf->totalSize % inf->pieceSize;
459    }
460    if( !*size )
461    {
462        *size = inf->pieceSize;
463    }
464
465    return readOrWriteBytes( io, offset, *size, buf, write );
466}
467
468static void invertSlots( tr_io_t * io, int slot1, int slot2 )
469{
470    tr_torrent_t * tor = io->tor;
471    tr_info_t    * inf = &tor->info;
472
473    uint8_t * buf1, * buf2;
474    int piece1, piece2, foo;
475
476    buf1 = calloc( inf->pieceSize, 1 );
477    buf2 = calloc( inf->pieceSize, 1 );
478
479    readSlot( io, slot1, buf1, &foo );
480    readSlot( io, slot2, buf2, &foo );
481
482    writeSlot( io, slot1, buf2, &foo );
483    writeSlot( io, slot2, buf1, &foo );
484
485    free( buf1 );
486    free( buf2 );
487
488    piece1                = io->slotPiece[slot1];
489    piece2                = io->slotPiece[slot2];
490    io->slotPiece[slot1]  = piece2;
491    io->slotPiece[slot2]  = piece1;
492    if( piece1 >= 0 )
493    {
494        io->pieceSlot[piece1] = slot2;
495    }
496    if( piece2 >= 0 )
497    {
498        io->pieceSlot[piece2] = slot1;
499    }
500}
501
502static void reorderPieces( tr_io_t * io )
503{
504    tr_torrent_t * tor = io->tor;
505    tr_info_t    * inf = &tor->info;
506
507    int i, didInvert;
508
509    /* Try to move pieces to their final places */
510    do
511    {
512        didInvert = 0;
513
514        for( i = 0; i < inf->pieceCount; i++ )
515        {
516            if( io->pieceSlot[i] < 0 )
517            {
518                /* We haven't started this piece yet */
519                continue;
520            }
521            if( io->pieceSlot[i] == i )
522            {
523                /* Already in place */
524                continue;
525            }
526            if( i >= io->slotsUsed )
527            {
528                /* File is not big enough yet */
529                continue;
530            }
531
532            /* Move piece i into slot i */
533            tr_inf( "invert %d and %d", io->pieceSlot[i], i );
534            invertSlots( io, i, io->pieceSlot[i] );
535            didInvert = 1;
536        }
537    } while( didInvert );
538}
539
540static void findSlotForPiece( tr_io_t * io, int piece )
541{
542    int i;
543#if 0
544    tr_torrent_t * tor = io->tor;
545    tr_info_t    * inf = &tor->info;
546
547    tr_dbg( "Entering findSlotForPiece" );
548
549    for( i = 0; i < inf->pieceCount; i++ )
550        printf( "%02d ", io->slotPiece[i] );
551    printf( "\n" );
552    for( i = 0; i < inf->pieceCount; i++ )
553        printf( "%02d ", io->pieceSlot[i] );
554    printf( "\n" );
555#endif
556
557    /* Look for an empty slot somewhere */
558    for( i = 0; i < io->slotsUsed; i++ )
559    {
560        if( io->slotPiece[i] < 0 )
561        {
562            io->pieceSlot[piece] = i;
563            io->slotPiece[i]     = piece;
564            goto reorder;
565        }
566    }
567
568    /* No empty slot, extend the file */
569    io->pieceSlot[piece]         = io->slotsUsed;
570    io->slotPiece[io->slotsUsed] = piece;
571    (io->slotsUsed)++;
572
573  reorder:
574    reorderPieces( io );
575
576#if 0
577    for( i = 0; i < inf->pieceCount; i++ )
578        printf( "%02d ", io->slotPiece[i] );
579    printf( "\n" );
580    for( i = 0; i < inf->pieceCount; i++ )
581        printf( "%02d ", io->pieceSlot[i] );
582    printf( "\n" );
583
584    printf( "Leaving findSlotForPiece\n" );
585#endif
586}
Note: See TracBrowser for help on using the repository browser.