source: trunk/libtransmission/inout.c @ 310

Last change on this file since 310 was 310, checked in by joshe, 15 years ago

Add support to libtransmission and the MacOS X GUI for saving private copies of

torrent files.

  • Property svn:keywords set to Date Rev Author Id
File size: 16.3 KB
Line 
1/******************************************************************************
2 * $Id: inout.c 310 2006-06-09 19:53:35Z joshe $
3 *
4 * Copyright (c) 2005-2006 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include "transmission.h"
26
27struct tr_io_s
28{
29    tr_torrent_t * tor;
30
31    /* Position of pieces
32       -1 = we haven't started to download this piece yet
33        n = we have started or completed the piece in slot n */
34    int         * pieceSlot;
35
36    /* Pieces in slot
37       -1 = unused slot
38        n = piece n */
39    int         * slotPiece;
40
41    int           slotsUsed;
42};
43
44#include "fastresume.h"
45
46/***********************************************************************
47 * Local prototypes
48 **********************************************************************/
49static int  createFiles( tr_io_t * );
50static int  checkFiles( tr_io_t * );
51static void closeFiles( tr_io_t * );
52static int  readOrWriteBytes( tr_io_t *, uint64_t, int, uint8_t *, int );
53static int  readOrWriteSlot( tr_io_t * io, int slot, uint8_t * buf,
54                             int * size, int write );
55static void findSlotForPiece( tr_io_t *, int );
56
57#define readBytes(io,o,s,b)  readOrWriteBytes(io,o,s,b,0)
58#define writeBytes(io,o,s,b) readOrWriteBytes(io,o,s,b,1)
59
60#define readSlot(io,sl,b,s)  readOrWriteSlot(io,sl,b,s,0)
61#define writeSlot(io,sl,b,s) readOrWriteSlot(io,sl,b,s,1)
62
63/***********************************************************************
64 * tr_ioInit
65 ***********************************************************************
66 * Open all files we are going to write to
67 **********************************************************************/
68tr_io_t * tr_ioInit( tr_torrent_t * tor )
69{
70    tr_io_t * io;
71
72    io      = malloc( sizeof( tr_io_t ) );
73    io->tor = tor;
74
75    if( createFiles( io ) || checkFiles( io ) )
76    {
77        free( io );
78        return NULL;
79    }
80
81    return io;
82}
83
84/***********************************************************************
85 * tr_ioRead
86 ***********************************************************************
87 *
88 **********************************************************************/
89int tr_ioRead( tr_io_t * io, int index, int begin, int length,
90               uint8_t * buf )
91{
92    uint64_t    offset;
93    tr_info_t * inf = &io->tor->info;
94
95    offset = (uint64_t) io->pieceSlot[index] *
96        (uint64_t) inf->pieceSize + (uint64_t) begin;
97
98    return readBytes( io, offset, length, buf );
99}
100
101/***********************************************************************
102 * tr_ioWrite
103 ***********************************************************************
104 *
105 **********************************************************************/
106int tr_ioWrite( tr_io_t * io, int index, int begin, int length,
107                uint8_t * buf )
108{
109    tr_torrent_t * tor = io->tor;
110    tr_info_t    * inf = &io->tor->info;
111    uint64_t       offset;
112    int            i, hashFailed;
113    uint8_t        hash[SHA_DIGEST_LENGTH];
114    uint8_t      * pieceBuf;
115    int            pieceSize;
116    int            startBlock, endBlock;
117
118    if( io->pieceSlot[index] < 0 )
119    {
120        findSlotForPiece( io, index );
121        tr_inf( "Piece %d: starting in slot %d", index,
122                io->pieceSlot[index] );
123    }
124
125    offset = (uint64_t) io->pieceSlot[index] *
126        (uint64_t) inf->pieceSize + (uint64_t) begin;
127
128    if( writeBytes( io, offset, length, buf ) )
129    {
130        return 1;
131    }
132
133    startBlock = tr_pieceStartBlock( index );
134    endBlock   = startBlock + tr_pieceCountBlocks( index );
135    for( i = startBlock; i < endBlock; i++ )
136    {
137        if( !tr_cpBlockIsComplete( tor->completion, i ) )
138        {
139            /* The piece is not complete */
140            return 0;
141        }
142    }
143
144    /* The piece is complete, check the hash */
145    pieceSize = tr_pieceSize( index );
146    pieceBuf  = malloc( pieceSize );
147    readBytes( io, (uint64_t) io->pieceSlot[index] *
148               (uint64_t) inf->pieceSize, pieceSize, pieceBuf );
149    SHA1( pieceBuf, pieceSize, hash );
150    free( pieceBuf );
151
152    hashFailed = memcmp( hash, &inf->pieces[20*index], SHA_DIGEST_LENGTH );
153    if( hashFailed )
154    {
155        tr_inf( "Piece %d (slot %d): hash FAILED", index,
156                io->pieceSlot[index] );
157
158        /* We will need to reload the whole piece */
159        for( i = startBlock; i < endBlock; i++ )
160        {
161            tr_cpBlockRem( tor->completion, i );
162        }
163    }
164    else
165    {
166        tr_inf( "Piece %d (slot %d): hash OK", index,
167                io->pieceSlot[index] );
168        tr_cpPieceAdd( tor->completion, index );
169    }
170
171    /* Assign blame or credit to peers */
172    for( i = 0; i < tor->peerCount; i++ )
173    {
174        tr_peerBlame( tor, tor->peers[i], index, !hashFailed );
175    }
176
177    return 0;
178}
179
180void tr_ioClose( tr_io_t * io )
181{
182    closeFiles( io );
183
184    fastResumeSave( io );
185
186    free( io->pieceSlot );
187    free( io->slotPiece );
188    free( io );
189}
190
191void tr_ioSaveResume( tr_io_t * io )
192{
193    fastResumeSave( io );
194}
195
196/***********************************************************************
197 * createFiles
198 ***********************************************************************
199 * Make sure the existing folders/files have correct types and
200 * permissions, and create missing folders and files
201 **********************************************************************/
202static int createFiles( tr_io_t * io )
203{
204    tr_torrent_t * tor = io->tor;
205    tr_info_t    * inf = &tor->info;
206
207    int           i;
208    char        * path, * p;
209    struct stat   sb;
210    int           file;
211
212    tr_dbg( "Creating files..." );
213
214    for( i = 0; i < inf->fileCount; i++ )
215    {
216        asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
217
218        /* Create folders */
219        if( NULL != ( p = strrchr( path, '/' ) ) )
220        {
221            *p = '\0';
222            if( tr_mkdir( path ) )
223            {
224                free( path );
225                return 1;
226            }
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, 0666 ) ) < 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.