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

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

(1.0x) backport the Verify fixes from r5042 (don't lose partial blocks when verifying local data) and r5046 (some torrents were unnecessarily rechecked at the beginning of their second session).

  • Property svn:keywords set to Date Rev Author Id
File size: 12.1 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 5047 2008-02-16 06:12:03Z 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 err = checkPiece( tor, i );
279
280            if( !err ) /* yay */
281            {
282                tr_torrentSetHasPiece( tor, i, TRUE );
283            }
284            else
285            {
286                /* if we were wrong about it being complete,
287                * reset and start again.  if we were right about
288                * it being incomplete, do nothing -- we don't
289                * want to lose blocks in those incomplete pieces */
290
291                if( tr_cpPieceIsComplete( tor->completion, i ) )
292                    tr_torrentSetHasPiece( tor, i, FALSE );
293            }
294        }
295
296        tr_torrentSetPieceChecked( tor, i, TRUE );
297    }
298}
299
300/**
301***
302**/
303
304int
305tr_ioHash( tr_torrent * tor, int pieceIndex )
306{
307    int ret;
308    const int success = !checkPiece( tor, pieceIndex );
309
310    if( success )
311    {
312        tr_dbg( "Piece %d hash OK", pieceIndex );
313        tr_cpPieceAdd( tor->completion, pieceIndex );
314        ret = TR_OK;
315    }
316    else
317    {
318        tr_err( "Piece %d hash FAILED", pieceIndex );
319        tr_cpPieceRem( tor->completion, pieceIndex );
320        ret = TR_ERROR;
321    }
322
323    tr_peerMgrSetBlame( tor->handle->peerMgr, tor->info.hash,
324                        pieceIndex, success );
325
326    return ret;
327}
328
329/**
330***
331**/
332
333struct recheck_node
334{
335    tr_torrent * torrent;
336    tr_recheck_done_cb recheck_done_cb;
337};
338
339static void
340fireCheckDone( tr_torrent          * torrent,
341               tr_recheck_done_cb    recheck_done_cb )
342{
343    if( recheck_done_cb != NULL )
344        (*recheck_done_cb)( torrent );
345}
346
347static struct recheck_node currentNode;
348
349static tr_list * recheckList = NULL;
350
351static tr_thread * recheckThread = NULL;
352
353static int stopCurrent = FALSE;
354
355static tr_lock* getRecheckLock( void )
356{
357    static tr_lock * lock = NULL;
358    if( lock == NULL )
359        lock = tr_lockNew( );
360    return lock;
361}
362
363static void
364recheckThreadFunc( void * unused UNUSED )
365{
366    for( ;; )
367    {
368        int i;
369        tr_torrent * tor;
370        struct recheck_node * node;
371
372        tr_lockLock( getRecheckLock( ) );
373        stopCurrent = FALSE;
374        node = (struct recheck_node*) recheckList ? recheckList->data : NULL;
375        if( node == NULL ) {
376            currentNode.torrent = NULL;
377            break;
378        }
379
380        currentNode = *node;
381        tor = currentNode.torrent;
382        tr_list_remove_data( &recheckList, node );
383        tr_free( node );
384        tr_lockUnlock( getRecheckLock( ) );
385
386        tor->recheckState = TR_RECHECK_NOW;
387
388        /* remove the unchecked pieces from completion... */
389        for( i=0; i<tor->info.pieceCount; ++i ) 
390            if( !tr_torrentIsPieceChecked( tor, i ) )
391                tr_cpPieceRem( tor->completion, i );
392
393        tr_inf( "Verifying some pieces of \"%s\"", tor->info.name );
394        for( i=0; i<tor->info.fileCount && !stopCurrent; ++i )
395            checkFile( tor, i, &stopCurrent );
396
397        tor->recheckState = TR_RECHECK_NONE;
398
399        if( !stopCurrent )
400        {
401            tr_fastResumeSave( tor );
402            fireCheckDone( tor, currentNode.recheck_done_cb );
403        }
404    }
405
406    recheckThread = NULL;
407    tr_lockUnlock( getRecheckLock( ) );
408}
409
410void
411tr_ioRecheckAdd( tr_torrent          * tor,
412                 tr_recheck_done_cb    recheck_done_cb )
413{
414    const int uncheckedCount = tr_torrentCountUncheckedPieces( tor );
415
416    if( !uncheckedCount )
417    {
418        /* doesn't need to be checked... */
419        recheck_done_cb( tor );
420    }
421    else
422    {
423        struct recheck_node * node;
424
425        tr_inf( "Queueing %s to verify %d local file pieces", tor->info.name, uncheckedCount );
426
427        node = tr_new( struct recheck_node, 1 );
428        node->torrent = tor;
429        node->recheck_done_cb = recheck_done_cb;
430
431        tr_lockLock( getRecheckLock( ) );
432        tor->recheckState = recheckList ? TR_RECHECK_WAIT : TR_RECHECK_NOW;
433        tr_list_append( &recheckList, node );
434        if( recheckThread == NULL )
435            recheckThread = tr_threadNew( recheckThreadFunc, NULL, "recheckThreadFunc" );
436        tr_lockUnlock( getRecheckLock( ) );
437    }
438}
439
440static int
441compareRecheckByTorrent( const void * va, const void * vb )
442{
443    const struct recheck_node * a = va;
444    const tr_torrent * b = vb;
445    return a->torrent - b;
446}
447
448void
449tr_ioRecheckRemove( tr_torrent * tor )
450{
451    tr_lock * lock = getRecheckLock( );
452    tr_lockLock( lock );
453
454    if( tor == currentNode.torrent )
455    {
456        stopCurrent = TRUE;
457        while( stopCurrent )
458        {
459            tr_lockUnlock( lock );
460            tr_wait( 100 );
461            tr_lockLock( lock );
462        }
463    }
464    else
465    {
466        tr_free( tr_list_remove( &recheckList, tor, compareRecheckByTorrent ) );
467        tor->recheckState = TR_RECHECK_NONE;
468    }
469
470    tr_lockUnlock( lock );
471}
Note: See TracBrowser for help on using the repository browser.