source: trunk/libtransmission/verify.c @ 10186

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

(trunk libT) use valloc() and st.st_blksize when verifying local data

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