~ubuntu-branches/ubuntu/maverick/transmission/maverick

« back to all changes in this revision

Viewing changes to libtransmission/inout.c

  • Committer: Bazaar Package Importer
  • Author(s): Philipp Benner
  • Date: 2006-08-29 20:50:41 UTC
  • Revision ID: james.westby@ubuntu.com-20060829205041-mzhpiqksdf7pbk17
Tags: upstream-0.6.1.dfsg
ImportĀ upstreamĀ versionĀ 0.6.1.dfsg

Show diffs side-by-side

added added

removed removed

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