source: branches/save_torrent_files/libtransmission/inout.c @ 289

Last change on this file since 289 was 289, checked in by joshe, 16 years ago

Create any needed parent directories for the
prefs, cache, and torrents directories.

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