source: trunk/libtransmission/inout.c @ 1074

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

Handle torrents with empty folders (fixes #12)

  • Property svn:keywords set to Date Rev Author Id
File size: 17.3 KB
Line 
1/******************************************************************************
2 * $Id: inout.c 1074 2006-11-10 21:30:32Z titer $
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        /* Empty folders use a dummy "" file, skip those */
254        if( p == &path[strlen( path ) - 1] )
255        {
256            free( path );
257            continue;
258        }
259
260        if( stat( path, &sb ) )
261        {
262            /* File doesn't exist yet */
263            if( ( file = open( path, O_WRONLY|O_CREAT|O_TRUNC, 0666 ) ) < 0 )
264            {
265                tr_err( "Could not create `%s' (%s)", path,
266                        strerror( errno ) );
267                free( path );
268                return 1;
269            }
270            close( file );
271        }
272        else if( ( sb.st_mode & S_IFMT ) != S_IFREG )
273        {
274            /* Node exists but isn't a file */
275            /* XXX this should be reported to the frontend somehow */
276            tr_err( "Remove %s, it's in the way.", path );
277            free( path );
278            return 1;
279        }
280
281        free( path );
282    }
283
284    return 0;
285}
286
287/***********************************************************************
288 * checkFiles
289 ***********************************************************************
290 * Look for pieces
291 **********************************************************************/
292static int checkFiles( tr_io_t * io )
293{
294    tr_torrent_t * tor = io->tor;
295    tr_info_t    * inf = &tor->info;
296
297    int i;
298    uint8_t * buf;
299    uint8_t hash[SHA_DIGEST_LENGTH];
300
301    io->pieceSlot = malloc( inf->pieceCount * sizeof( int ) );
302    io->slotPiece = malloc( inf->pieceCount * sizeof( int ) );
303
304    if( !fastResumeLoad( io ) )
305    {
306        return 0;
307    }
308
309    tr_dbg( "Checking pieces..." );
310
311    /* Yet we don't have anything */
312    memset( io->pieceSlot, 0xFF, inf->pieceCount * sizeof( int ) );
313    memset( io->slotPiece, 0xFF, inf->pieceCount * sizeof( int ) );
314
315    /* Check pieces */
316    io->slotsUsed = 0;
317    buf           = malloc( inf->pieceSize );
318    for( i = 0; i < inf->pieceCount; i++ )
319    {
320        int size, j;
321
322        if( readSlot( io, i, buf, &size ) )
323        {
324            break;
325        }
326
327        io->slotsUsed = i + 1;
328        SHA1( buf, size, hash );
329
330        for( j = i; j < inf->pieceCount - 1; j++ )
331        {
332            if( !memcmp( hash, &inf->pieces[20*j], SHA_DIGEST_LENGTH ) )
333            {
334                io->pieceSlot[j] = i;
335                io->slotPiece[i] = j;
336
337                tr_cpPieceAdd( tor->completion, j );
338                break;
339            }
340        }
341
342        if( io->slotPiece[i] > -1 )
343        {
344            continue;
345        }
346
347        /* Special case for the last piece */
348        SHA1( buf, tr_pieceSize( inf->pieceCount - 1 ), hash );
349        if( !memcmp( hash, &inf->pieces[20 * (inf->pieceCount - 1)],
350                     SHA_DIGEST_LENGTH ) )
351        {
352            io->pieceSlot[inf->pieceCount - 1] = i;
353            io->slotPiece[i]                   = inf->pieceCount - 1;
354
355            tr_cpPieceAdd( tor->completion, inf->pieceCount - 1 );
356        }
357    }
358    free( buf );
359
360    return 0;
361}
362
363/***********************************************************************
364 * closeFiles
365 **********************************************************************/
366static void closeFiles( tr_io_t * io )
367{
368    tr_torrent_t * tor = io->tor;
369    tr_info_t    * inf = &tor->info;
370
371    int i;
372    char * path;
373
374    for( i = 0; i < inf->fileCount; i++ )
375    {
376        asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
377        tr_fdFileClose( tor->fdlimit, path );
378        free( path );
379    }
380}
381
382/***********************************************************************
383 * readOrWriteBytes
384 ***********************************************************************
385 *
386 **********************************************************************/
387typedef size_t (* iofunc) ( int, void *, size_t );
388static int readOrWriteBytes( tr_io_t * io, uint64_t offset, int size,
389                             uint8_t * buf, int isWrite )
390{
391    tr_torrent_t * tor = io->tor;
392    tr_info_t    * inf = &tor->info;
393
394    int    piece = offset / inf->pieceSize;
395    int    begin = offset % inf->pieceSize;
396    int    i;
397    size_t cur;
398    char * path;
399    int    file;
400    iofunc readOrWrite = isWrite ? (iofunc) write : (iofunc) read;
401
402    /* Release the torrent lock so the UI can still update itself if
403       this blocks for a while */
404    tr_lockUnlock( &tor->lock );
405
406    /* We don't ever read or write more than a piece at a time */
407    if( tr_pieceSize( piece ) < begin + size )
408    {
409        tr_err( "readOrWriteBytes: trying to write more than a piece" );
410        goto fail;
411    }
412
413    /* Find which file we shall start reading/writing in */
414    for( i = 0; i < inf->fileCount; i++ )
415    {
416        if( offset < inf->files[i].length )
417        {
418            /* This is the file */
419            break;
420        }
421        offset -= inf->files[i].length;
422    }
423    if( i >= inf->fileCount )
424    {
425        /* Should not happen */
426        tr_err( "readOrWriteBytes: offset out of range (%"PRIu64", %d, %d)",
427                offset, size, isWrite );
428        goto fail;
429    }
430
431    while( size > 0 )
432    {
433        /* How much can we put or take with this file */
434        if( inf->files[i].length < offset + size )
435        {
436            cur = (int) ( inf->files[i].length - offset );
437        }
438        else
439        {
440            cur = size;
441        }
442
443        if( cur > 0 )
444        {
445            /* Now let's get a stream on the file... */
446            asprintf( &path, "%s/%s", tor->destination, inf->files[i].name );
447            file = tr_fdFileOpen( tor->fdlimit, path );
448            if( file < 0 )
449            {
450                tr_err( "readOrWriteBytes: could not open file '%s'", path );
451                free( path );
452                goto fail;
453            }
454            free( path );
455
456            /* seek to the right offset... */
457            if( lseek( file, offset, SEEK_SET ) < 0 )
458            {
459                goto fail;
460            }
461
462            /* do what we are here to do... */
463            if( readOrWrite( file, buf, cur ) != cur )
464            {
465                goto fail;
466            }
467
468            /* and close the stream. */
469            tr_fdFileRelease( tor->fdlimit, file );
470        }
471
472        /* 'cur' bytes done, 'size - cur' bytes to go with the next file */
473        i      += 1;
474        offset  = 0;
475        size   -= cur;
476        buf    += cur;
477    }
478
479    tr_lockLock( &tor->lock );
480    return 0;
481
482fail:
483    tr_lockLock( &tor->lock );
484    return 1;
485}
486
487/***********************************************************************
488 * readSlot
489 ***********************************************************************
490 *
491 **********************************************************************/
492static int readOrWriteSlot( tr_io_t * io, int slot, uint8_t * buf,
493                            int * size, int write )
494{
495    tr_torrent_t * tor = io->tor;
496    tr_info_t    * inf = &tor->info;
497
498    uint64_t offset = (uint64_t) slot * (uint64_t) inf->pieceSize;
499   
500    *size = 0;
501    if( slot == inf->pieceCount - 1 )
502    {
503        *size = inf->totalSize % inf->pieceSize;
504    }
505    if( !*size )
506    {
507        *size = inf->pieceSize;
508    }
509
510    return readOrWriteBytes( io, offset, *size, buf, write );
511}
512
513static void invertSlots( tr_io_t * io, int slot1, int slot2 )
514{
515    tr_torrent_t * tor = io->tor;
516    tr_info_t    * inf = &tor->info;
517
518    uint8_t * buf1, * buf2;
519    int piece1, piece2, foo;
520
521    buf1 = calloc( inf->pieceSize, 1 );
522    buf2 = calloc( inf->pieceSize, 1 );
523
524    readSlot( io, slot1, buf1, &foo );
525    readSlot( io, slot2, buf2, &foo );
526
527    writeSlot( io, slot1, buf2, &foo );
528    writeSlot( io, slot2, buf1, &foo );
529
530    free( buf1 );
531    free( buf2 );
532
533    piece1                = io->slotPiece[slot1];
534    piece2                = io->slotPiece[slot2];
535    io->slotPiece[slot1]  = piece2;
536    io->slotPiece[slot2]  = piece1;
537    if( piece1 >= 0 )
538    {
539        io->pieceSlot[piece1] = slot2;
540    }
541    if( piece2 >= 0 )
542    {
543        io->pieceSlot[piece2] = slot1;
544    }
545}
546
547static void reorderPieces( tr_io_t * io )
548{
549    tr_torrent_t * tor = io->tor;
550    tr_info_t    * inf = &tor->info;
551
552    int i, didInvert;
553
554    /* Try to move pieces to their final places */
555    do
556    {
557        didInvert = 0;
558
559        for( i = 0; i < inf->pieceCount; i++ )
560        {
561            if( io->pieceSlot[i] < 0 )
562            {
563                /* We haven't started this piece yet */
564                continue;
565            }
566            if( io->pieceSlot[i] == i )
567            {
568                /* Already in place */
569                continue;
570            }
571            if( i >= io->slotsUsed )
572            {
573                /* File is not big enough yet */
574                continue;
575            }
576
577            /* Move piece i into slot i */
578            tr_inf( "invert %d and %d", io->pieceSlot[i], i );
579            invertSlots( io, i, io->pieceSlot[i] );
580            didInvert = 1;
581        }
582    } while( didInvert );
583}
584
585static void findSlotForPiece( tr_io_t * io, int piece )
586{
587    int i;
588#if 0
589    tr_torrent_t * tor = io->tor;
590    tr_info_t    * inf = &tor->info;
591
592    tr_dbg( "Entering findSlotForPiece" );
593
594    for( i = 0; i < inf->pieceCount; i++ )
595        printf( "%02d ", io->slotPiece[i] );
596    printf( "\n" );
597    for( i = 0; i < inf->pieceCount; i++ )
598        printf( "%02d ", io->pieceSlot[i] );
599    printf( "\n" );
600#endif
601
602    /* Look for an empty slot somewhere */
603    for( i = 0; i < io->slotsUsed; i++ )
604    {
605        if( io->slotPiece[i] < 0 )
606        {
607            io->pieceSlot[piece] = i;
608            io->slotPiece[i]     = piece;
609            goto reorder;
610        }
611    }
612
613    /* No empty slot, extend the file */
614    io->pieceSlot[piece]         = io->slotsUsed;
615    io->slotPiece[io->slotsUsed] = piece;
616    (io->slotsUsed)++;
617
618  reorder:
619    reorderPieces( io );
620
621#if 0
622    for( i = 0; i < inf->pieceCount; i++ )
623        printf( "%02d ", io->slotPiece[i] );
624    printf( "\n" );
625    for( i = 0; i < inf->pieceCount; i++ )
626        printf( "%02d ", io->pieceSlot[i] );
627    printf( "\n" );
628
629    printf( "Leaving findSlotForPiece\n" );
630#endif
631}
Note: See TracBrowser for help on using the repository browser.