source: branches/0.9x/libtransmission/inout.c @ 3649

Last change on this file since 3649 was 3649, checked in by charles, 15 years ago

part 1 of the data corruption fix: plug the leaky abstraction in fdlimit

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