source: trunk/libtransmission/verify.c @ 10689

Last change on this file since 10689 was 10689, checked in by charles, 12 years ago

(trunk libT) #3231 "Check existing data is extremely slow" -- increase the read buffer size as suggested by eridius

  • Property svn:keywords set to Date Rev Author Id
File size: 10.2 KB
Line 
1/*
2 * This file Copyright (C) 2007-2010 Mnemosyne LLC
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: verify.c 10689 2010-05-25 20:19:34Z charles $
11 */
12
13#include <unistd.h> /* S_ISREG */
14#include <sys/stat.h>
15
16#ifdef HAVE_POSIX_FADVISE
17 #define _XOPEN_SOURCE 600
18#endif
19#if defined(HAVE_POSIX_FADVISE) || defined(SYS_DARWIN)
20 #include <fcntl.h> /* posix_fadvise() / fcntl() */
21#endif
22
23#include <openssl/sha.h>
24
25#include "transmission.h"
26#include "completion.h"
27#include "fdlimit.h"
28#include "inout.h"
29#include "list.h"
30#include "platform.h"
31#include "torrent.h"
32#include "utils.h" /* tr_buildPath */
33#include "verify.h"
34
35
36/***
37****
38***/
39
40enum
41{
42    MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY = 100
43};
44
45/* #define STOPWATCH */
46
47static tr_bool
48verifyTorrent( tr_torrent * tor, tr_bool * stopFlag )
49{
50    SHA_CTX sha;
51    int fd = -1;
52    int64_t filePos = 0;
53    tr_bool changed = 0;
54    tr_bool hadPiece = 0;
55    time_t lastSleptAt = 0;
56    uint32_t piecePos = 0;
57    uint32_t pieceBytesRead = 0;
58    tr_file_index_t fileIndex = 0;
59    tr_file_index_t prevFileIndex = !fileIndex;
60    tr_piece_index_t pieceIndex = 0;
61    const time_t begin = tr_time( );
62    time_t end;
63    //const int64_t buflen = 1024 * 128; /* 128 KiB buffer */
64    const int64_t buflen = 1024 * 1024;
65    uint8_t * buffer = tr_valloc( buflen );
66
67    SHA1_Init( &sha );
68
69    while( !*stopFlag && ( pieceIndex < tor->info.pieceCount ) )
70    {
71        int64_t leftInPiece;
72        int64_t leftInFile;
73        int64_t bytesThisPass;
74        const tr_file * file = &tor->info.files[fileIndex];
75
76        /* if we're starting a new piece... */
77        if( piecePos == 0 )
78        {
79            hadPiece = tr_cpPieceIsComplete( &tor->completion, pieceIndex );
80            /* fprintf( stderr, "starting piece %d of %d\n", (int)pieceIndex, (int)tor->info.pieceCount ); */
81        }
82
83        /* if we're starting a new file... */
84        if( !filePos && (fd<0) && (fileIndex!=prevFileIndex) )
85        {
86            char * filename = tr_torrentFindFile( tor, fileIndex );
87            fd = filename == NULL ? -1 : tr_open_file_for_scanning( filename );
88            /* fprintf( stderr, "opening file #%d (%s) -- %d\n", fileIndex, filename, fd ); */
89            tr_free( filename );
90            prevFileIndex = fileIndex;
91        }
92
93        /* figure out how much we can read this pass */
94        leftInPiece = tr_torPieceCountBytes( tor, pieceIndex ) - piecePos;
95        leftInFile = file->length - filePos;
96        bytesThisPass = MIN( leftInFile, leftInPiece );
97        bytesThisPass = MIN( bytesThisPass, buflen );
98        /* fprintf( stderr, "reading this pass: %d\n", (int)bytesThisPass ); */
99
100        /* read a bit */
101        if( fd >= 0 ) {
102            const ssize_t numRead = tr_pread( fd, buffer, bytesThisPass, filePos );
103            if( numRead == bytesThisPass )
104                SHA1_Update( &sha, buffer, numRead );
105            if( numRead > 0 ) {
106                pieceBytesRead += numRead;
107#if defined HAVE_POSIX_FADVISE && defined POSIX_FADV_DONTNEED
108                posix_fadvise( fd, filePos, bytesThisPass, POSIX_FADV_DONTNEED );
109#endif
110            }
111        }
112
113        /* move our offsets */
114        leftInPiece -= bytesThisPass;
115        leftInFile -= bytesThisPass;
116        piecePos += bytesThisPass;
117        filePos += bytesThisPass;
118
119        /* if we're finishing a piece... */
120        if( leftInPiece == 0 )
121        {
122            time_t now;
123            tr_bool hasPiece;
124            uint8_t hash[SHA_DIGEST_LENGTH];
125
126            SHA1_Final( hash, &sha );
127            hasPiece = !memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
128            /* fprintf( stderr, "do the hashes match? %s\n", (hasPiece?"yes":"no") ); */
129
130            if( hasPiece ) {
131                tr_torrentSetHasPiece( tor, pieceIndex, TRUE );
132                if( !hadPiece )
133                    changed = TRUE;
134            } else if( hadPiece ) {
135                tr_torrentSetHasPiece( tor, pieceIndex, FALSE );
136                changed = TRUE;
137            }
138            tr_torrentSetPieceChecked( tor, pieceIndex, TRUE );
139            now = tr_time( );
140            tor->anyDate = now;
141
142            /* sleeping even just a few msec per second goes a long
143             * way towards reducing IO load... */
144            if( lastSleptAt != now ) {
145                lastSleptAt = now;
146                tr_wait_msec( MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY );
147            }
148
149            SHA1_Init( &sha );
150            ++pieceIndex;
151            piecePos = 0;
152            pieceBytesRead = 0;
153        }
154
155        /* if we're finishing a file... */
156        if( leftInFile == 0 )
157        {
158            /* fprintf( stderr, "closing file\n" ); */
159            if( fd >= 0 ) { tr_close_file( fd ); fd = -1; }
160            ++fileIndex;
161            filePos = 0;
162        }
163    }
164
165    /* cleanup */
166    if( fd >= 0 )
167        tr_close_file( fd );
168    free( buffer );
169
170    /* stopwatch */
171    end = tr_time( );
172    tr_tordbg( tor, "it took %d seconds to verify %"PRIu64" bytes (%"PRIu64" bytes per second)",
173               (int)(end-begin), tor->info.totalSize, (uint64_t)(tor->info.totalSize/(1+(end-begin))) );
174
175    return changed;
176}
177
178/***
179****
180***/
181
182struct verify_node
183{
184    tr_torrent *         torrent;
185    tr_verify_done_cb    verify_done_cb;
186};
187
188static void
189fireCheckDone( tr_torrent * tor, tr_verify_done_cb verify_done_cb )
190{
191    assert( tr_isTorrent( tor ) );
192
193    if( verify_done_cb )
194        verify_done_cb( tor );
195}
196
197static struct verify_node currentNode;
198static tr_list * verifyList = NULL;
199static tr_thread * verifyThread = NULL;
200static tr_bool stopCurrent = FALSE;
201
202static tr_lock*
203getVerifyLock( void )
204{
205    static tr_lock * lock = NULL;
206
207    if( lock == NULL )
208        lock = tr_lockNew( );
209    return lock;
210}
211
212static void
213verifyThreadFunc( void * unused UNUSED )
214{
215    for( ;; )
216    {
217        int                  changed = 0;
218        tr_torrent         * tor;
219        struct verify_node * node;
220
221        tr_lockLock( getVerifyLock( ) );
222        stopCurrent = FALSE;
223        node = (struct verify_node*) verifyList ? verifyList->data : NULL;
224        if( node == NULL )
225        {
226            currentNode.torrent = NULL;
227            break;
228        }
229
230        currentNode = *node;
231        tor = currentNode.torrent;
232        tr_list_remove_data( &verifyList, node );
233        tr_free( node );
234        tr_lockUnlock( getVerifyLock( ) );
235
236        tr_torinf( tor, "%s", _( "Verifying torrent" ) );
237        tr_torrentSetVerifyState( tor, TR_VERIFY_NOW );
238        changed = verifyTorrent( tor, &stopCurrent );
239        tr_torrentSetVerifyState( tor, TR_VERIFY_NONE );
240        assert( tr_isTorrent( tor ) );
241
242        if( !stopCurrent )
243        {
244            if( changed )
245                tr_torrentSetDirty( tor );
246            fireCheckDone( tor, currentNode.verify_done_cb );
247        }
248    }
249
250    verifyThread = NULL;
251    tr_lockUnlock( getVerifyLock( ) );
252}
253
254static tr_bool
255torrentHasAnyLocalData( const tr_torrent * tor )
256{
257    tr_file_index_t i;
258    tr_bool hasAny = FALSE;
259    const tr_file_index_t n = tor->info.fileCount;
260
261    assert( tr_isTorrent( tor ) );
262
263    for( i=0; i<n && !hasAny; ++i )
264    {
265        struct stat sb;
266        char * path = tr_torrentFindFile( tor, i );
267        if( ( path != NULL ) && !stat( path, &sb ) && ( sb.st_size > 0 ) )
268            hasAny = TRUE;
269        tr_free( path );
270    }
271
272    return hasAny;
273}
274
275void
276tr_verifyAdd( tr_torrent *      tor,
277              tr_verify_done_cb verify_done_cb )
278{
279    assert( tr_isTorrent( tor ) );
280
281    if( tr_torrentCountUncheckedPieces( tor ) == 0 )
282    {
283        /* doesn't need to be checked... */
284        fireCheckDone( tor, verify_done_cb );
285    }
286    else if( !torrentHasAnyLocalData( tor ) )
287    {
288        /* we haven't downloaded anything for this torrent yet...
289         * no need to leave it waiting in the back of the queue.
290         * we can mark it as all-missing from here and fire
291         * the "done" callback */
292        const tr_bool hadAny = tr_cpHaveTotal( &tor->completion ) != 0;
293        tr_piece_index_t i;
294        for( i=0; i<tor->info.pieceCount; ++i ) {
295            tr_torrentSetHasPiece( tor, i, FALSE );
296            tr_torrentSetPieceChecked( tor, i, TRUE );
297        }
298        if( hadAny ) /* if we thought we had some, flag as dirty */
299            tr_torrentSetDirty( tor );
300        fireCheckDone( tor, verify_done_cb );
301    }
302    else
303    {
304        struct verify_node * node;
305
306        tr_torinf( tor, "%s", _( "Queued for verification" ) );
307
308        node = tr_new( struct verify_node, 1 );
309        node->torrent = tor;
310        node->verify_done_cb = verify_done_cb;
311
312        tr_lockLock( getVerifyLock( ) );
313        tr_torrentSetVerifyState( tor, TR_VERIFY_WAIT );
314        tr_list_append( &verifyList, node );
315        if( verifyThread == NULL )
316            verifyThread = tr_threadNew( verifyThreadFunc, NULL );
317        tr_lockUnlock( getVerifyLock( ) );
318    }
319}
320
321static int
322compareVerifyByTorrent( const void * va,
323                        const void * vb )
324{
325    const struct verify_node * a = va;
326    const tr_torrent *         b = vb;
327
328    return a->torrent - b;
329}
330
331tr_bool
332tr_verifyInProgress( const tr_torrent * tor )
333{
334    tr_bool found = FALSE;
335    tr_lock * lock = getVerifyLock( );
336    tr_lockLock( lock );
337
338    assert( tr_isTorrent( tor ) );
339
340    found = ( tor == currentNode.torrent )
341         || ( tr_list_find( verifyList, tor, compareVerifyByTorrent ) != NULL );
342
343    tr_lockUnlock( lock );
344    return found;
345}
346
347void
348tr_verifyRemove( tr_torrent * tor )
349{
350    tr_lock * lock = getVerifyLock( );
351    tr_lockLock( lock );
352
353    assert( tr_isTorrent( tor ) );
354
355    if( tor == currentNode.torrent )
356    {
357        stopCurrent = TRUE;
358        while( stopCurrent )
359        {
360            tr_lockUnlock( lock );
361            tr_wait_msec( 100 );
362            tr_lockLock( lock );
363        }
364    }
365    else
366    {
367        tr_free( tr_list_remove( &verifyList, tor, compareVerifyByTorrent ) );
368        tr_torrentSetVerifyState( tor, TR_VERIFY_NONE );
369    }
370
371    tr_lockUnlock( lock );
372}
373
374void
375tr_verifyClose( tr_session * session UNUSED )
376{
377    tr_lockLock( getVerifyLock( ) );
378
379    stopCurrent = TRUE;
380    tr_list_free( &verifyList, tr_free );
381
382    tr_lockUnlock( getVerifyLock( ) );
383}
Note: See TracBrowser for help on using the repository browser.