source: trunk/libtransmission/verify.c @ 10187

Last change on this file since 10187 was 10187, checked in by charles, 13 years ago

(trunk libT) minor tweak to verify: use tr_pread() instead of lseek+read

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