source: trunk/libtransmission/verify.c @ 10256

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

(trunk libT) #2952: "When possible, use posix_memalign() instead of valloc()" -- fixed in trunk for 1.91

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