source: trunk/libtransmission/verify.c @ 9791

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

(trunk libT) try not to cache file data read in during verification

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