source: trunk/libtransmission/inout.c @ 20

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

Update 2005-12-25

File size: 16.2 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 **********************************************************************/
355typedef size_t (* iofunc) ( void *, size_t, size_t, FILE * );
356static int readOrWriteBytes( tr_io_t * io, uint64_t offset, int size,
357                             uint8_t * buf, int write )
358{
359    tr_torrent_t * tor = io->tor;
360    tr_info_t    * inf = &tor->info;
361
362    int    piece = offset / inf->pieceSize;
363    int    begin = offset % inf->pieceSize;
364    int    i, cur;
365    char * path;
366    FILE * file;
367    iofunc readOrWrite = write ? (iofunc) fwrite : (iofunc) fread;
368
369    /* Release the torrent lock so the UI can still update itself if
370       this blocks for a while */
371    tr_lockUnlock( &tor->lock );
372
373    /* We don't ever read or write more than a piece at a time */
374    if( tr_pieceSize( piece ) < begin + size )
375    {
376        tr_err( "readOrWriteBytes: trying to write more than a piece" );
377        goto fail;
378    }
379
380    /* Find which file we shall start reading/writing in */
381    for( i = 0; i < inf->fileCount; i++ )
382    {
383        if( offset < inf->files[i].length )
384        {
385            /* This is the file */
386            break;
387        }
388        offset -= inf->files[i].length;
389    }
390    if( i >= inf->fileCount )
391    {
392        /* Should not happen */
393        tr_err( "readOrWriteBytes: offset out of range" );
394        goto fail;
395    }
396
397    while( size > 0 )
398    {
399        /* How much can we put or take with this file */
400        if( inf->files[i].length < offset + size )
401        {
402            cur = (int) ( inf->files[i].length - offset );
403        }
404        else
405        {
406            cur = size;
407        }
408
409        /* Now let's get a stream on the file... */
410        asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
411        file = tr_fdFileOpen( tor->fdlimit, path );
412        if( !file )
413        {
414            tr_err( "readOrWriteBytes: could not open file '%s'", path );
415            free( path );
416            goto fail;
417        }
418        free( path );
419
420        /* seek to the right offset... */
421        if( fseeko( file, offset, SEEK_SET ) )
422        {
423            goto fail;
424        }
425
426        /* do what we are here to do... */
427        if( readOrWrite( buf, cur, 1, file ) != 1 )
428        {
429            goto fail;
430        }
431
432        /* and close the stream. */
433        tr_fdFileRelease( tor->fdlimit, file );
434
435        /* 'cur' bytes done, 'size - cur' bytes to go with the next file */
436        i      += 1;
437        offset  = 0;
438        size   -= cur;
439        buf    += cur;
440    }
441
442    tr_lockLock( &tor->lock );
443    return 0;
444
445fail:
446    tr_lockLock( &tor->lock );
447    return 1;
448}
449
450/***********************************************************************
451 * readSlot
452 ***********************************************************************
453 *
454 **********************************************************************/
455static int readOrWriteSlot( tr_io_t * io, int slot, uint8_t * buf,
456                            int * size, int write )
457{
458    tr_torrent_t * tor = io->tor;
459    tr_info_t    * inf = &tor->info;
460
461    uint64_t offset = (uint64_t) slot * (uint64_t) inf->pieceSize;
462   
463    *size = 0;
464    if( slot == inf->pieceCount - 1 )
465    {
466        *size = inf->totalSize % inf->pieceSize;
467    }
468    if( !*size )
469    {
470        *size = inf->pieceSize;
471    }
472
473    return readOrWriteBytes( io, offset, *size, buf, write );
474}
475
476static void invertSlots( tr_io_t * io, int slot1, int slot2 )
477{
478    tr_torrent_t * tor = io->tor;
479    tr_info_t    * inf = &tor->info;
480
481    uint8_t * buf1, * buf2;
482    int piece1, piece2, foo;
483
484    buf1 = calloc( inf->pieceSize, 1 );
485    buf2 = calloc( inf->pieceSize, 1 );
486
487    readSlot( io, slot1, buf1, &foo );
488    readSlot( io, slot2, buf2, &foo );
489
490    writeSlot( io, slot1, buf2, &foo );
491    writeSlot( io, slot2, buf1, &foo );
492
493    free( buf1 );
494    free( buf2 );
495
496    piece1                = io->slotPiece[slot1];
497    piece2                = io->slotPiece[slot2];
498    io->slotPiece[slot1]  = piece2;
499    io->slotPiece[slot2]  = piece1;
500    if( piece1 >= 0 )
501    {
502        io->pieceSlot[piece1] = slot2;
503    }
504    if( piece2 >= 0 )
505    {
506        io->pieceSlot[piece2] = slot1;
507    }
508}
509
510static void reorderPieces( tr_io_t * io )
511{
512    tr_torrent_t * tor = io->tor;
513    tr_info_t    * inf = &tor->info;
514
515    int i, didInvert;
516
517    /* Try to move pieces to their final places */
518    do
519    {
520        didInvert = 0;
521
522        for( i = 0; i < inf->pieceCount; i++ )
523        {
524            if( io->pieceSlot[i] < 0 )
525            {
526                /* We haven't started this piece yet */
527                continue;
528            }
529            if( io->pieceSlot[i] == i )
530            {
531                /* Already in place */
532                continue;
533            }
534            if( i >= io->slotsUsed )
535            {
536                /* File is not big enough yet */
537                continue;
538            }
539
540            /* Move piece i into slot i */
541            tr_inf( "invert %d and %d", io->pieceSlot[i], i );
542            invertSlots( io, i, io->pieceSlot[i] );
543            didInvert = 1;
544        }
545    } while( didInvert );
546}
547
548static void findSlotForPiece( tr_io_t * io, int piece )
549{
550    int i;
551#if 0
552    tr_torrent_t * tor = io->tor;
553    tr_info_t    * inf = &tor->info;
554
555    tr_dbg( "Entering findSlotForPiece" );
556
557    for( i = 0; i < inf->pieceCount; i++ )
558        printf( "%02d ", io->slotPiece[i] );
559    printf( "\n" );
560    for( i = 0; i < inf->pieceCount; i++ )
561        printf( "%02d ", io->pieceSlot[i] );
562    printf( "\n" );
563#endif
564
565    /* Look for an empty slot somewhere */
566    for( i = 0; i < io->slotsUsed; i++ )
567    {
568        if( io->slotPiece[i] < 0 )
569        {
570            io->pieceSlot[piece] = i;
571            io->slotPiece[i]     = piece;
572            goto reorder;
573        }
574    }
575
576    /* No empty slot, extend the file */
577    io->pieceSlot[piece]         = io->slotsUsed;
578    io->slotPiece[io->slotsUsed] = piece;
579    (io->slotsUsed)++;
580
581  reorder:
582    reorderPieces( io );
583
584#if 0
585    for( i = 0; i < inf->pieceCount; i++ )
586        printf( "%02d ", io->slotPiece[i] );
587    printf( "\n" );
588    for( i = 0; i < inf->pieceCount; i++ )
589        printf( "%02d ", io->pieceSlot[i] );
590    printf( "\n" );
591
592    printf( "Leaving findSlotForPiece\n" );
593#endif
594}
Note: See TracBrowser for help on using the repository browser.