source: trunk/libtransmission/inout.c @ 920

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

Merge nat-traversal branch to trunk.

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