source: branches/io/libtransmission/inout.c @ 1204

Last change on this file since 1204 was 1204, checked in by titer, 15 years ago

Typo, cleaning

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