source: branches/1.0x/libtransmission/inout.c @ 4776

Last change on this file since 4776 was 4776, checked in by charles, 14 years ago

(1.0x) finish the trunk/1.0x libT merge: router death & portability

  • Property svn:keywords set to Date Rev Author Id
File size: 11.7 KB
Line 
1/*
2 * This file Copyright (C) 2007-2008 Charles Kerr <charles@rebelbase.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: inout.c 4776 2008-01-21 02:07:55Z charles $
11 */
12
13#include <assert.h>
14#include <stdlib.h>
15#include <string.h>
16
17#include <sys/types.h>
18#include <sys/stat.h>
19#include <unistd.h>
20
21#include "transmission.h"
22#include "completion.h"
23#include "crypto.h"
24#include "fastresume.h"
25#include "fdlimit.h"
26#include "inout.h"
27#include "list.h"
28#include "platform.h"
29#include "peer-mgr.h"
30#include "stats.h"
31#include "torrent.h"
32#include "utils.h"
33
34/****
35*****  Low-level IO functions
36****/
37
38#ifdef WIN32
39#define lseek _lseeki64
40#endif
41
42enum { TR_IO_READ, TR_IO_WRITE };
43
44static int
45readOrWriteBytes( const tr_torrent  * tor,
46                  int                 ioMode,
47                  int                 fileIndex,
48                  uint64_t            fileOffset,
49                  void              * buf,
50                  size_t              buflen )
51{
52    const tr_info * info = &tor->info;
53    const tr_file * file = &info->files[fileIndex];
54    typedef size_t (* iofunc) ( int, void *, size_t );
55    iofunc func = ioMode == TR_IO_READ ? (iofunc)read : (iofunc)write;
56    char path[MAX_PATH_LENGTH];
57    struct stat sb;
58    int fd = -1;
59    int err;
60    int fileExists;
61
62    assert( 0<=fileIndex && fileIndex<info->fileCount );
63    assert( !file->length || (fileOffset < file->length));
64    assert( fileOffset + buflen <= file->length );
65
66    tr_buildPath ( path, sizeof(path), tor->destination, file->name, NULL );
67    fileExists = !stat( path, &sb );
68
69    if( !file->length )
70        return TR_OK;
71
72    if ((ioMode==TR_IO_READ) && !fileExists ) /* does file exist? */
73        err = tr_ioErrorFromErrno ();
74    else if ((fd = tr_fdFileCheckout ( path, ioMode==TR_IO_WRITE )) < 0)
75        err = fd;
76    else if( lseek( fd, (off_t)fileOffset, SEEK_SET ) == ((off_t)-1) )
77        err = tr_ioErrorFromErrno( );
78    else if( func( fd, buf, buflen ) != buflen )
79        err = tr_ioErrorFromErrno( );
80    else
81        err = TR_OK;
82
83    if( ( err==TR_OK ) && ( !fileExists ) && ( ioMode == TR_IO_WRITE) )
84        tr_statsFileCreated( tor->handle );
85 
86    if( fd >= 0 )
87        tr_fdFileReturn( fd );
88
89    return err;
90}
91
92static void
93findFileLocation( const tr_torrent * tor,
94                  int                pieceIndex,
95                  int                pieceOffset,
96                  int              * fileIndex,
97                  uint64_t         * fileOffset )
98{
99    const tr_info * info = &tor->info;
100
101    int i;
102    uint64_t piecePos = ((uint64_t)pieceIndex * info->pieceSize) + pieceOffset;
103
104    assert( 0<=pieceIndex && pieceIndex < info->pieceCount );
105    assert( 0<=tor->info.pieceSize );
106    assert( pieceOffset < tr_torPieceCountBytes( tor, pieceIndex ) );
107    assert( piecePos < info->totalSize );
108
109    for( i=0; info->files[i].length<=piecePos; ++i )
110        piecePos -= info->files[i].length;
111
112    *fileIndex = i;
113    *fileOffset = piecePos;
114
115    assert( 0<=*fileIndex && *fileIndex<info->fileCount );
116    assert( *fileOffset < info->files[i].length );
117}
118
119#ifdef WIN32
120static int
121ensureMinimumFileSize( const tr_torrent  * tor,
122                       int                 fileIndex,
123                       uint64_t            minBytes )
124{
125    int fd;
126    int err;
127    struct stat sb;
128    const tr_file * file = &tor->info.files[fileIndex];
129    char path[MAX_PATH_LENGTH];
130
131    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
132    assert( minBytes <= file->length );
133
134    tr_buildPath( path, sizeof(path), tor->destination, file->name, NULL );
135
136    fd = tr_fdFileCheckout( path, TRUE );
137    if( fd < 0 ) /* bad fd */
138        err = fd;
139    else if (fstat (fd, &sb) ) /* how big is the file? */
140        err = tr_ioErrorFromErrno ();
141    else if (sb.st_size >= (off_t)minBytes) /* already big enough */
142        err = TR_OK;
143    else if (!ftruncate( fd, minBytes )) /* grow it */
144        err = TR_OK;
145    else /* couldn't grow it */
146        err = tr_ioErrorFromErrno ();
147
148    if( fd >= 0 )
149        tr_fdFileReturn( fd );
150
151    return err;
152}
153#endif
154
155static int
156readOrWritePiece( tr_torrent  * tor,
157                  int           ioMode,
158                  int           pieceIndex,
159                  int           pieceOffset,
160                  uint8_t     * buf,
161                  size_t        buflen )
162{
163    int err = 0;
164    int fileIndex;
165    uint64_t fileOffset;
166    const tr_info * info = &tor->info;
167
168    assert( 0<=pieceIndex && pieceIndex<tor->info.pieceCount );
169    assert( buflen <= (size_t) tr_torPieceCountBytes( tor, pieceIndex ) );
170
171    findFileLocation ( tor, pieceIndex, pieceOffset, &fileIndex, &fileOffset );
172
173    while( buflen && !err )
174    {
175        const tr_file * file = &info->files[fileIndex];
176        const uint64_t bytesThisPass = MIN( buflen, file->length - fileOffset );
177
178#ifdef WIN32
179        if( ioMode == TR_IO_WRITE )
180            err = ensureMinimumFileSize( tor, fileIndex,
181                                         fileOffset + bytesThisPass );
182        if( !err )
183#endif
184            err = readOrWriteBytes( tor, ioMode,
185                                    fileIndex, fileOffset, buf, bytesThisPass );
186        buf += bytesThisPass;
187        buflen -= bytesThisPass;
188        fileIndex++;
189        fileOffset = 0;
190    }
191
192    return err;
193}
194
195int
196tr_ioRead( tr_torrent  * tor,
197           int           pieceIndex,
198           int           begin,
199           int           len,
200           uint8_t     * buf )
201{
202    return readOrWritePiece( tor, TR_IO_READ, pieceIndex, begin, buf, len );
203}
204
205int
206tr_ioWrite( tr_torrent     * tor,
207            int              pieceIndex,
208            int              begin,
209            int              len,
210            const uint8_t  * buf )
211{
212    return readOrWritePiece( tor, TR_IO_WRITE, pieceIndex, begin, (void*)buf, len );
213}
214
215/****
216*****
217****/
218
219static int
220tr_ioRecalculateHash( tr_torrent  * tor,
221                      int           pieceIndex,
222                      uint8_t     * setme )
223{
224    int n;
225    int ret;
226    uint8_t * buf;
227    const tr_info * info;
228
229    assert( tor != NULL );
230    assert( setme != NULL );
231    assert( 0<=pieceIndex && pieceIndex<tor->info.pieceCount );
232
233    info = &tor->info;
234    n = tr_torPieceCountBytes( tor, pieceIndex );
235
236    buf = tr_new( uint8_t, n );
237    ret = tr_ioRead( tor, pieceIndex, 0, n, buf );
238    if( !ret )
239        tr_sha1( setme, buf, n, NULL );
240    tr_free( buf );
241
242    return ret;
243}
244
245static int
246checkPiece( tr_torrent * tor, int pieceIndex )
247{
248    uint8_t hash[SHA_DIGEST_LENGTH];
249    const int ret = tr_ioRecalculateHash( tor, pieceIndex, hash )
250        || memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
251    tr_dbg ("torrent [%s] piece %d hash check: %s",
252            tor->info.name, pieceIndex, ( ret ? "FAILED" : "OK" ));
253    return ret;
254}
255
256static void
257checkFile( tr_torrent   * tor,
258           int            fileIndex,
259           int          * abortFlag )
260{
261    int i;
262    int nofile;
263    struct stat sb;
264    char path[MAX_PATH_LENGTH];
265    const tr_file * file = &tor->info.files[fileIndex];
266
267    tr_buildPath ( path, sizeof(path), tor->destination, file->name, NULL );
268    nofile = stat( path, &sb ) || !S_ISREG( sb.st_mode );
269
270    for( i=file->firstPiece; i<=file->lastPiece && i<tor->info.pieceCount && (!*abortFlag); ++i )
271    {
272        if( nofile )
273        {
274            tr_torrentSetHasPiece( tor, i, 0 );
275        }
276        else if( !tr_torrentIsPieceChecked( tor, i ) )
277        {
278            const int check = checkPiece( tor, i );
279            tr_torrentSetHasPiece( tor, i, !check );
280            tr_torrentSetPieceChecked( tor, i, TRUE );
281        }
282    }
283}
284
285/**
286***
287**/
288
289int
290tr_ioHash( tr_torrent * tor, int pieceIndex )
291{
292    int ret;
293    const int success = !checkPiece( tor, pieceIndex );
294
295    if( success )
296    {
297        tr_dbg( "Piece %d hash OK", pieceIndex );
298        tr_cpPieceAdd( tor->completion, pieceIndex );
299        ret = TR_OK;
300    }
301    else
302    {
303        tr_err( "Piece %d hash FAILED", pieceIndex );
304        tr_cpPieceRem( tor->completion, pieceIndex );
305        ret = TR_ERROR;
306    }
307
308    tr_peerMgrSetBlame( tor->handle->peerMgr, tor->info.hash,
309                        pieceIndex, success );
310
311    return ret;
312}
313
314/**
315***
316**/
317
318struct recheck_node
319{
320    tr_torrent * torrent;
321    tr_recheck_done_cb recheck_done_cb;
322};
323
324static void
325fireCheckDone( tr_torrent          * torrent,
326               tr_recheck_done_cb    recheck_done_cb )
327{
328    if( recheck_done_cb != NULL )
329        (*recheck_done_cb)( torrent );
330}
331
332static struct recheck_node currentNode;
333
334static tr_list * recheckList = NULL;
335
336static tr_thread * recheckThread = NULL;
337
338static int stopCurrent = FALSE;
339
340static tr_lock* getRecheckLock( void )
341{
342    static tr_lock * lock = NULL;
343    if( lock == NULL )
344        lock = tr_lockNew( );
345    return lock;
346}
347
348static void
349recheckThreadFunc( void * unused UNUSED )
350{
351    for( ;; )
352    {
353        int i;
354        tr_torrent * tor;
355        struct recheck_node * node;
356
357        tr_lockLock( getRecheckLock( ) );
358        stopCurrent = FALSE;
359        node = (struct recheck_node*) recheckList ? recheckList->data : NULL;
360        if( node == NULL ) {
361            currentNode.torrent = NULL;
362            break;
363        }
364
365        currentNode = *node;
366        tor = currentNode.torrent;
367        tr_list_remove_data( &recheckList, node );
368        tr_free( node );
369        tr_lockUnlock( getRecheckLock( ) );
370
371        tor->recheckState = TR_RECHECK_NOW;
372
373        /* remove the unchecked pieces from completion... */
374        for( i=0; i<tor->info.pieceCount; ++i ) 
375            if( !tr_torrentIsPieceChecked( tor, i ) )
376                tr_cpPieceRem( tor->completion, i );
377
378        tr_inf( "Verifying some pieces of \"%s\"", tor->info.name );
379        for( i=0; i<tor->info.fileCount && !stopCurrent; ++i )
380            checkFile( tor, i, &stopCurrent );
381
382        tor->recheckState = TR_RECHECK_NONE;
383
384        if( !stopCurrent )
385        {
386            tr_fastResumeSave( tor );
387            fireCheckDone( tor, currentNode.recheck_done_cb );
388        }
389    }
390
391    recheckThread = NULL;
392    tr_lockUnlock( getRecheckLock( ) );
393}
394
395void
396tr_ioRecheckAdd( tr_torrent          * tor,
397                 tr_recheck_done_cb    recheck_done_cb )
398{
399    const int uncheckedCount = tr_torrentCountUncheckedPieces( tor );
400
401    if( !uncheckedCount )
402    {
403        /* doesn't need to be checked... */
404        recheck_done_cb( tor );
405    }
406    else
407    {
408        struct recheck_node * node;
409
410        tr_inf( "Queueing %s to verify %d local file pieces", tor->info.name, uncheckedCount );
411
412        node = tr_new( struct recheck_node, 1 );
413        node->torrent = tor;
414        node->recheck_done_cb = recheck_done_cb;
415
416        tr_lockLock( getRecheckLock( ) );
417        tor->recheckState = recheckList ? TR_RECHECK_WAIT : TR_RECHECK_NOW;
418        tr_list_append( &recheckList, node );
419        if( recheckThread == NULL )
420            recheckThread = tr_threadNew( recheckThreadFunc, NULL, "recheckThreadFunc" );
421        tr_lockUnlock( getRecheckLock( ) );
422    }
423}
424
425static int
426compareRecheckByTorrent( const void * va, const void * vb )
427{
428    const struct recheck_node * a = va;
429    const tr_torrent * b = vb;
430    return a->torrent - b;
431}
432
433void
434tr_ioRecheckRemove( tr_torrent * tor )
435{
436    tr_lock * lock = getRecheckLock( );
437    tr_lockLock( lock );
438
439    if( tor == currentNode.torrent )
440    {
441        stopCurrent = TRUE;
442        while( stopCurrent )
443        {
444            tr_lockUnlock( lock );
445            tr_wait( 100 );
446            tr_lockLock( lock );
447        }
448    }
449    else
450    {
451        tr_free( tr_list_remove( &recheckList, tor, compareRecheckByTorrent ) );
452        tor->recheckState = TR_RECHECK_NONE;
453    }
454
455    tr_lockUnlock( lock );
456}
Note: See TracBrowser for help on using the repository browser.