source: trunk/libtransmission/inout.c @ 238

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

Use file descriptors instead of streams (preliminary cleanup for fixes
to come in fdlimit*)

File size: 16.3 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;
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    if( memcmp( hash, &inf->pieces[20*index], SHA_DIGEST_LENGTH ) )
151    {
152        tr_inf( "Piece %d (slot %d): hash FAILED", index,
153                io->pieceSlot[index] );
154
155        /* We will need to reload the whole piece */
156        for( i = startBlock; i < endBlock; i++ )
157        {
158            tr_cpBlockRem( tor->completion, i );
159        }
160    }
161    else
162    {
163        tr_inf( "Piece %d (slot %d): hash OK", index,
164                io->pieceSlot[index] );
165        tr_cpPieceAdd( tor->completion, index );
166    }
167
168    return 0;
169}
170
171void tr_ioClose( tr_io_t * io )
172{
173    closeFiles( io );
174
175    fastResumeSave( io );
176
177    free( io->pieceSlot );
178    free( io->slotPiece );
179    free( io );
180}
181
182void tr_ioSaveResume( tr_io_t * io )
183{
184    fastResumeSave( 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    int           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 = open( path, O_WRONLY|O_CREAT|O_TRUNC ) ) < 0 )
234            {
235                tr_err( "Could not create `%s' (%s)", path,
236                        strerror( errno ) );
237                free( path );
238                return 1;
239            }
240            close( 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
270    io->pieceSlot = malloc( inf->pieceCount * sizeof( int ) );
271    io->slotPiece = malloc( inf->pieceCount * sizeof( int ) );
272
273    if( !fastResumeLoad( io ) )
274    {
275        return 0;
276    }
277
278    tr_dbg( "Checking pieces..." );
279
280    /* Yet we don't have anything */
281    memset( io->pieceSlot, 0xFF, inf->pieceCount * sizeof( int ) );
282    memset( io->slotPiece, 0xFF, inf->pieceCount * sizeof( int ) );
283
284    /* Check pieces */
285    io->slotsUsed = 0;
286    buf           = malloc( inf->pieceSize );
287    for( i = 0; i < inf->pieceCount; i++ )
288    {
289        int size, j;
290
291        if( readSlot( io, i, buf, &size ) )
292        {
293            break;
294        }
295
296        io->slotsUsed = i + 1;
297        SHA1( buf, size, hash );
298
299        for( j = i; j < inf->pieceCount - 1; j++ )
300        {
301            if( !memcmp( hash, &inf->pieces[20*j], SHA_DIGEST_LENGTH ) )
302            {
303                io->pieceSlot[j] = i;
304                io->slotPiece[i] = j;
305
306                tr_cpPieceAdd( tor->completion, j );
307                break;
308            }
309        }
310
311        if( io->slotPiece[i] > -1 )
312        {
313            continue;
314        }
315
316        /* Special case for the last piece */
317        SHA1( buf, tr_pieceSize( inf->pieceCount - 1 ), hash );
318        if( !memcmp( hash, &inf->pieces[20 * (inf->pieceCount - 1)],
319                     SHA_DIGEST_LENGTH ) )
320        {
321            io->pieceSlot[inf->pieceCount - 1] = i;
322            io->slotPiece[i]                   = inf->pieceCount - 1;
323
324            tr_cpPieceAdd( tor->completion, inf->pieceCount - 1 );
325        }
326    }
327    free( buf );
328
329    return 0;
330}
331
332/***********************************************************************
333 * closeFiles
334 **********************************************************************/
335static void closeFiles( tr_io_t * io )
336{
337    tr_torrent_t * tor = io->tor;
338    tr_info_t    * inf = &tor->info;
339
340    int i;
341    char * path;
342
343    for( i = 0; i < inf->fileCount; i++ )
344    {
345        asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
346        tr_fdFileClose( tor->fdlimit, path );
347        free( path );
348    }
349}
350
351/***********************************************************************
352 * readOrWriteBytes
353 ***********************************************************************
354 *
355 **********************************************************************/
356typedef size_t (* iofunc) ( int, void *, size_t );
357static int readOrWriteBytes( tr_io_t * io, uint64_t offset, int size,
358                             uint8_t * buf, int isWrite )
359{
360    tr_torrent_t * tor = io->tor;
361    tr_info_t    * inf = &tor->info;
362
363    int    piece = offset / inf->pieceSize;
364    int    begin = offset % inf->pieceSize;
365    int    i;
366    size_t cur;
367    char * path;
368    int    file;
369    iofunc readOrWrite = isWrite ? (iofunc) write : (iofunc) read;
370
371    /* Release the torrent lock so the UI can still update itself if
372       this blocks for a while */
373    tr_lockUnlock( &tor->lock );
374
375    /* We don't ever read or write more than a piece at a time */
376    if( tr_pieceSize( piece ) < begin + size )
377    {
378        tr_err( "readOrWriteBytes: trying to write more than a piece" );
379        goto fail;
380    }
381
382    /* Find which file we shall start reading/writing in */
383    for( i = 0; i < inf->fileCount; i++ )
384    {
385        if( offset < inf->files[i].length )
386        {
387            /* This is the file */
388            break;
389        }
390        offset -= inf->files[i].length;
391    }
392    if( i >= inf->fileCount )
393    {
394        /* Should not happen */
395        tr_err( "readOrWriteBytes: offset out of range (%lld, %d, %d)",
396                offset, size, write );
397        goto fail;
398    }
399
400    while( size > 0 )
401    {
402        /* How much can we put or take with this file */
403        if( inf->files[i].length < offset + size )
404        {
405            cur = (int) ( inf->files[i].length - offset );
406        }
407        else
408        {
409            cur = size;
410        }
411
412        /* Now let's get a stream on the file... */
413        asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
414        file = tr_fdFileOpen( tor->fdlimit, path );
415        if( file < 0 )
416        {
417            tr_err( "readOrWriteBytes: could not open file '%s'", path );
418            free( path );
419            goto fail;
420        }
421        free( path );
422
423        /* seek to the right offset... */
424        if( lseek( file, offset, SEEK_SET ) < 0 )
425        {
426            goto fail;
427        }
428
429        /* do what we are here to do... */
430        if( readOrWrite( file, buf, cur ) != cur )
431        {
432            goto fail;
433        }
434
435        /* and close the stream. */
436        tr_fdFileRelease( tor->fdlimit, file );
437
438        /* 'cur' bytes done, 'size - cur' bytes to go with the next file */
439        i      += 1;
440        offset  = 0;
441        size   -= cur;
442        buf    += cur;
443    }
444
445    tr_lockLock( &tor->lock );
446    return 0;
447
448fail:
449    tr_lockLock( &tor->lock );
450    return 1;
451}
452
453/***********************************************************************
454 * readSlot
455 ***********************************************************************
456 *
457 **********************************************************************/
458static int readOrWriteSlot( tr_io_t * io, int slot, uint8_t * buf,
459                            int * size, int write )
460{
461    tr_torrent_t * tor = io->tor;
462    tr_info_t    * inf = &tor->info;
463
464    uint64_t offset = (uint64_t) slot * (uint64_t) inf->pieceSize;
465   
466    *size = 0;
467    if( slot == inf->pieceCount - 1 )
468    {
469        *size = inf->totalSize % inf->pieceSize;
470    }
471    if( !*size )
472    {
473        *size = inf->pieceSize;
474    }
475
476    return readOrWriteBytes( io, offset, *size, buf, write );
477}
478
479static void invertSlots( tr_io_t * io, int slot1, int slot2 )
480{
481    tr_torrent_t * tor = io->tor;
482    tr_info_t    * inf = &tor->info;
483
484    uint8_t * buf1, * buf2;
485    int piece1, piece2, foo;
486
487    buf1 = calloc( inf->pieceSize, 1 );
488    buf2 = calloc( inf->pieceSize, 1 );
489
490    readSlot( io, slot1, buf1, &foo );
491    readSlot( io, slot2, buf2, &foo );
492
493    writeSlot( io, slot1, buf2, &foo );
494    writeSlot( io, slot2, buf1, &foo );
495
496    free( buf1 );
497    free( buf2 );
498
499    piece1                = io->slotPiece[slot1];
500    piece2                = io->slotPiece[slot2];
501    io->slotPiece[slot1]  = piece2;
502    io->slotPiece[slot2]  = piece1;
503    if( piece1 >= 0 )
504    {
505        io->pieceSlot[piece1] = slot2;
506    }
507    if( piece2 >= 0 )
508    {
509        io->pieceSlot[piece2] = slot1;
510    }
511}
512
513static void reorderPieces( tr_io_t * io )
514{
515    tr_torrent_t * tor = io->tor;
516    tr_info_t    * inf = &tor->info;
517
518    int i, didInvert;
519
520    /* Try to move pieces to their final places */
521    do
522    {
523        didInvert = 0;
524
525        for( i = 0; i < inf->pieceCount; i++ )
526        {
527            if( io->pieceSlot[i] < 0 )
528            {
529                /* We haven't started this piece yet */
530                continue;
531            }
532            if( io->pieceSlot[i] == i )
533            {
534                /* Already in place */
535                continue;
536            }
537            if( i >= io->slotsUsed )
538            {
539                /* File is not big enough yet */
540                continue;
541            }
542
543            /* Move piece i into slot i */
544            tr_inf( "invert %d and %d", io->pieceSlot[i], i );
545            invertSlots( io, i, io->pieceSlot[i] );
546            didInvert = 1;
547        }
548    } while( didInvert );
549}
550
551static void findSlotForPiece( tr_io_t * io, int piece )
552{
553    int i;
554#if 0
555    tr_torrent_t * tor = io->tor;
556    tr_info_t    * inf = &tor->info;
557
558    tr_dbg( "Entering findSlotForPiece" );
559
560    for( i = 0; i < inf->pieceCount; i++ )
561        printf( "%02d ", io->slotPiece[i] );
562    printf( "\n" );
563    for( i = 0; i < inf->pieceCount; i++ )
564        printf( "%02d ", io->pieceSlot[i] );
565    printf( "\n" );
566#endif
567
568    /* Look for an empty slot somewhere */
569    for( i = 0; i < io->slotsUsed; i++ )
570    {
571        if( io->slotPiece[i] < 0 )
572        {
573            io->pieceSlot[piece] = i;
574            io->slotPiece[i]     = piece;
575            goto reorder;
576        }
577    }
578
579    /* No empty slot, extend the file */
580    io->pieceSlot[piece]         = io->slotsUsed;
581    io->slotPiece[io->slotsUsed] = piece;
582    (io->slotsUsed)++;
583
584  reorder:
585    reorderPieces( io );
586
587#if 0
588    for( i = 0; i < inf->pieceCount; i++ )
589        printf( "%02d ", io->slotPiece[i] );
590    printf( "\n" );
591    for( i = 0; i < inf->pieceCount; i++ )
592        printf( "%02d ", io->pieceSlot[i] );
593    printf( "\n" );
594
595    printf( "Leaving findSlotForPiece\n" );
596#endif
597}
Note: See TracBrowser for help on using the repository browser.