source: trunk/libtransmission/verify.c @ 10264

Last change on this file since 10264 was 10264, checked in by livings124, 12 years ago

disable posix_memalign() on Mac - it requires 10.6

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