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

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

"corruption" fix part 3: cleanup. remove unneeded possible points of failure.

  • Property svn:keywords set to Date Rev Author Id
File size: 10.8 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 3665 2007-10-31 18:10:54Z 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 ) ) /* does file exist? */
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            minBytes )
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 ( minBytes <= 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)minBytes) /* already big enough */
140        ret = TR_OK;
141    else if (!ftruncate( fd, minBytes )) /* 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 = tr_new( uint8_t, n );
227    ret = tr_ioRead( tor, pieceIndex, 0, n, buf );
228    if( !ret )
229        tr_sha1( setme, buf, n, NULL );
230    tr_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    const 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,
270                        pieceIndex, success );
271
272    return ret;
273}
274
275/**
276***
277**/
278
279struct recheck_node
280{
281    tr_torrent * torrent;
282    tr_recheck_done_cb recheck_done_cb;
283};
284
285static void
286fireCheckDone( tr_torrent          * torrent,
287               tr_recheck_done_cb    recheck_done_cb )
288{
289    if( recheck_done_cb != NULL )
290        (*recheck_done_cb)( torrent );
291}
292
293static struct recheck_node currentNode;
294
295static tr_list * recheckList = NULL;
296
297static tr_thread * recheckThread = NULL;
298
299static int stopCurrent = FALSE;
300
301static tr_lock* getRecheckLock( void )
302{
303    static tr_lock * lock = NULL;
304    if( lock == NULL )
305        lock = tr_lockNew( );
306    return lock;
307}
308
309static void
310recheckThreadFunc( void * unused UNUSED )
311{
312    for( ;; )
313    {
314        int i;
315        tr_torrent * tor;
316        struct recheck_node * node;
317
318        tr_lockLock( getRecheckLock( ) );
319        stopCurrent = FALSE;
320        node = (struct recheck_node*) recheckList ? recheckList->data : NULL;
321        if( node == NULL ) {
322            currentNode.torrent = NULL;
323            break;
324        }
325
326        currentNode = *node;
327        tor = currentNode.torrent;
328        tr_list_remove_data( &recheckList, node );
329        tr_free( node );
330        tr_lockUnlock( getRecheckLock( ) );
331
332        if( tor->uncheckedPieces == NULL ) {
333            tor->recheckState = TR_RECHECK_NONE;
334            fireCheckDone( tor, currentNode.recheck_done_cb );
335            continue;
336        }
337
338        tor->recheckState = TR_RECHECK_NOW;
339
340        /* remove the unchecked pieces from completion... */
341        for( i=0; i<tor->info.pieceCount; ++i ) 
342            if( tr_bitfieldHas( tor->uncheckedPieces, i ) )
343                tr_cpPieceRem( tor->completion, i );
344
345        tr_inf( "Verifying some pieces of \"%s\"", tor->info.name );
346
347        for( i=0; i<tor->info.pieceCount && !stopCurrent; ++i ) 
348        {
349            if( !tr_bitfieldHas( tor->uncheckedPieces, i ) )
350                continue;
351
352            tr_torrentSetHasPiece( tor, i, !checkPiece( tor, i ) );
353            tr_bitfieldRem( tor->uncheckedPieces, i );
354        }
355
356        tor->recheckState = TR_RECHECK_NONE;
357
358        if( !stopCurrent )
359        {
360            tr_bitfieldFree( tor->uncheckedPieces );
361            tor->uncheckedPieces = NULL;
362            tr_fastResumeSave( tor );
363            fireCheckDone( tor, currentNode.recheck_done_cb );
364        }
365    }
366
367    recheckThread = NULL;
368    tr_lockUnlock( getRecheckLock( ) );
369}
370
371void
372tr_ioRecheckAdd( tr_torrent          * tor,
373                 tr_recheck_done_cb    recheck_done_cb )
374{
375    if( !tr_bitfieldCountTrueBits( tor->uncheckedPieces ) )
376    {
377        /* doesn't need to be checked... */
378        recheck_done_cb( tor );
379    }
380    else
381    {
382        struct recheck_node * node;
383
384        node = tr_new( struct recheck_node, 1 );
385        node->torrent = tor;
386        node->recheck_done_cb = recheck_done_cb;
387
388        tr_lockLock( getRecheckLock( ) );
389        tor->recheckState = recheckList ? TR_RECHECK_WAIT : TR_RECHECK_NOW;
390        tr_list_append( &recheckList, node );
391        if( recheckThread == NULL )
392            recheckThread = tr_threadNew( recheckThreadFunc, NULL, "recheckThreadFunc" );
393        tr_lockUnlock( getRecheckLock( ) );
394    }
395}
396
397static int
398compareRecheckByTorrent( const void * va, const void * vb )
399{
400    const struct recheck_node * a = va;
401    const tr_torrent * b = vb;
402    return a->torrent - b;
403}
404
405void
406tr_ioRecheckRemove( tr_torrent * tor )
407{
408    tr_lock * lock = getRecheckLock( );
409    tr_lockLock( lock );
410
411    if( tor == currentNode.torrent )
412    {
413        stopCurrent = TRUE;
414        while( stopCurrent )
415        {
416            tr_lockUnlock( lock );
417            tr_wait( 100 );
418            tr_lockLock( lock );
419        }
420    }
421    else
422    {
423        tr_free( tr_list_remove( &recheckList, tor, compareRecheckByTorrent ) );
424        tor->recheckState = TR_RECHECK_NONE;
425    }
426
427    tr_lockUnlock( lock );
428}
Note: See TracBrowser for help on using the repository browser.