source: trunk/libtransmission/verify.c @ 9819

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

#2696 posix_fadv_dontneed undeclared

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