source: trunk/libtransmission/verify.c @ 8291

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

(trunk libT) need feedback from Mac users on this change. On Linux, it gets rid of the inactive-memory-grows-during-torrent-verification behavior that's often reported as a bug.

  • Property svn:keywords set to Date Rev Author Id
File size: 8.2 KB
Line 
1/*
2 * This file Copyright (C) 2007-2009 Charles Kerr <charles@transmissionbt.com>
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 8291 2009-04-26 00:51:51Z 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 #include <fcntl.h> /* posix_fadvise() */
19#endif
20
21#include <openssl/sha.h>
22
23#include "transmission.h"
24#include "completion.h"
25#include "fdlimit.h"
26#include "resume.h" /* tr_torrentSaveResume() */
27#include "inout.h"
28#include "list.h"
29#include "platform.h"
30#include "torrent.h"
31#include "utils.h" /* tr_buildPath */
32#include "verify.h"
33
34
35/***
36****
37***/
38
39/* #define STOPWATCH */
40
41static tr_bool
42verifyTorrent( tr_torrent * tor, tr_bool * stopFlag )
43{
44    SHA_CTX sha;
45    FILE * fp = NULL;
46    size_t filePos = 0;
47    tr_bool changed = 0;
48    tr_bool hadPiece = 0;
49    uint32_t piecePos = 0;
50    tr_file_index_t fileIndex = 0;
51    tr_piece_index_t pieceIndex = 0;
52    const int64_t buflen = tor->info.pieceSize;
53    uint8_t * buffer = tr_new( uint8_t, buflen );
54#ifdef STOPWATCH
55    time_t now = time( NULL );
56#endif
57
58    SHA1_Init( &sha );
59
60    while( !*stopFlag && ( pieceIndex < tor->info.pieceCount ) )
61    {
62        int64_t leftInPiece;
63        int64_t leftInFile;
64        int64_t bytesThisPass;
65        const tr_file * file = &tor->info.files[fileIndex];
66
67        /* if we're starting a new piece... */
68        if( piecePos == 0 )
69        {
70            hadPiece = tr_cpPieceIsComplete( &tor->completion, pieceIndex );
71            /* fprintf( stderr, "starting piece %d of %d\n", (int)pieceIndex, (int)tor->info.pieceCount ); */
72        }
73
74        /* if we're starting a new file... */
75        if( !filePos && !fp )
76        {
77            char * filename = tr_buildPath( tor->downloadDir, file->name, NULL );
78            fp = tr_open_file_for_scanning( filename );
79            /* fprintf( stderr, "opening file #%d (%s) -- %p\n", fileIndex, filename, fp ); */
80            tr_free( filename );
81        }
82
83        /* figure out how much we can read this pass */
84        leftInPiece = tr_torPieceCountBytes( tor, pieceIndex ) - piecePos;
85        leftInFile = file->length - filePos;
86        bytesThisPass = MIN( leftInFile, leftInPiece );
87        bytesThisPass = MIN( bytesThisPass, buflen );
88        /* fprintf( stderr, "reading this pass: %d\n", (int)bytesThisPass ); */
89
90        /* read a bit */
91        if( fp && !fseek( fp, filePos, SEEK_SET ) ) {
92            const int64_t numRead = fread( buffer, 1, bytesThisPass, fp );
93            if( numRead == bytesThisPass )
94                SHA1_Update( &sha, buffer, numRead );
95        }
96
97        /* move our offsets */
98        leftInPiece -= bytesThisPass;
99        leftInFile -= bytesThisPass;
100        piecePos += bytesThisPass;
101        filePos += bytesThisPass;
102
103        /* if we're finishing a piece... */
104        if( leftInPiece == 0 )
105        {
106            tr_bool hasPiece;
107            uint8_t hash[SHA_DIGEST_LENGTH];
108
109            SHA1_Final( hash, &sha );
110            hasPiece = !memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
111            /* fprintf( stderr, "do the hashes match? %s\n", (hasPiece?"yes":"no") ); */
112
113            if( hasPiece ) {
114                tr_torrentSetHasPiece( tor, pieceIndex, TRUE );
115                if( !hadPiece )
116                    changed = TRUE;
117            } else if( hadPiece ) {
118                tr_torrentSetHasPiece( tor, pieceIndex, FALSE );
119                changed = TRUE;
120            }
121            tr_torrentSetPieceChecked( tor, pieceIndex, TRUE );
122
123            SHA1_Init( &sha );
124            ++pieceIndex;
125            piecePos = 0;
126        }
127
128        /* if we're finishing a file... */
129        if( leftInFile == 0 )
130        {
131            /* fprintf( stderr, "closing file\n" ); */
132            if( fp != NULL ) { tr_close_file( fp ); fp = NULL; }
133            ++fileIndex;
134            filePos = 0;
135        }
136    }
137
138    /* cleanup */
139    if( fp != NULL )
140        tr_close_file( fp );
141    tr_free( buffer );
142
143#ifdef STOPWATCH
144{
145    time_t now2 = time( NULL );
146    fprintf( stderr, "it took %d seconds to verify %"PRIu64" bytes (%"PRIu64" bytes per second)\n",
147             (int)(now2-now), tor->info.totalSize, (uint64_t)(tor->info.totalSize/(1+(now2-now))) );
148}
149#endif
150
151    return changed;
152}
153
154/***
155****
156***/
157
158struct verify_node
159{
160    tr_torrent *         torrent;
161    tr_verify_done_cb    verify_done_cb;
162};
163
164static void
165fireCheckDone( tr_torrent * tor, tr_verify_done_cb verify_done_cb )
166{
167    assert( tr_isTorrent( tor ) );
168
169    if( verify_done_cb )
170        verify_done_cb( tor );
171}
172
173static struct verify_node currentNode;
174static tr_list * verifyList = NULL;
175static tr_thread * verifyThread = NULL;
176static tr_bool stopCurrent = FALSE;
177
178static tr_lock*
179getVerifyLock( void )
180{
181    static tr_lock * lock = NULL;
182
183    if( lock == NULL )
184        lock = tr_lockNew( );
185    return lock;
186}
187
188static void
189verifyThreadFunc( void * unused UNUSED )
190{
191    for( ;; )
192    {
193        int                  changed = 0;
194        tr_torrent         * tor;
195        struct verify_node * node;
196
197        tr_lockLock( getVerifyLock( ) );
198        stopCurrent = FALSE;
199        node = (struct verify_node*) verifyList ? verifyList->data : NULL;
200        if( node == NULL )
201        {
202            currentNode.torrent = NULL;
203            break;
204        }
205
206        currentNode = *node;
207        tor = currentNode.torrent;
208        tr_list_remove_data( &verifyList, node );
209        tr_free( node );
210        tr_lockUnlock( getVerifyLock( ) );
211
212        tr_torinf( tor, _( "Verifying torrent" ) );
213        tr_torrentSetVerifyState( tor, TR_VERIFY_NOW );
214        changed = verifyTorrent( tor, &stopCurrent );
215        tr_torrentSetVerifyState( tor, TR_VERIFY_NONE );
216        assert( tr_isTorrent( tor ) );
217
218        if( !stopCurrent )
219        {
220            if( changed )
221                tr_torrentSaveResume( tor );
222            fireCheckDone( tor, currentNode.verify_done_cb );
223        }
224    }
225
226    verifyThread = NULL;
227    tr_lockUnlock( getVerifyLock( ) );
228}
229
230void
231tr_verifyAdd( tr_torrent *      tor,
232              tr_verify_done_cb verify_done_cb )
233{
234    const int uncheckedCount = tr_torrentCountUncheckedPieces( tor );
235
236    assert( tr_isTorrent( tor ) );
237
238    if( !uncheckedCount )
239    {
240        /* doesn't need to be checked... */
241        fireCheckDone( tor, verify_done_cb );
242    }
243    else
244    {
245        struct verify_node * node;
246
247        tr_torinf( tor, _( "Queued for verification" ) );
248
249        node = tr_new( struct verify_node, 1 );
250        node->torrent = tor;
251        node->verify_done_cb = verify_done_cb;
252
253        tr_lockLock( getVerifyLock( ) );
254        tr_torrentSetVerifyState( tor, verifyList ? TR_VERIFY_WAIT : TR_VERIFY_NOW );
255        tr_list_append( &verifyList, node );
256        if( verifyThread == NULL )
257            verifyThread = tr_threadNew( verifyThreadFunc, NULL );
258        tr_lockUnlock( getVerifyLock( ) );
259    }
260}
261
262static int
263compareVerifyByTorrent( const void * va,
264                        const void * vb )
265{
266    const struct verify_node * a = va;
267    const tr_torrent *         b = vb;
268
269    return a->torrent - b;
270}
271
272tr_bool
273tr_verifyInProgress( const tr_torrent * tor )
274{
275    tr_bool found = FALSE;
276    tr_lock * lock = getVerifyLock( );
277    tr_lockLock( lock );
278
279    assert( tr_isTorrent( tor ) );
280
281    found = ( tor == currentNode.torrent )
282         || ( tr_list_find( verifyList, tor, compareVerifyByTorrent ) != NULL );
283
284    tr_lockUnlock( lock );
285    return found;
286}
287
288void
289tr_verifyRemove( tr_torrent * tor )
290{
291    tr_lock * lock = getVerifyLock( );
292    tr_lockLock( lock );
293
294    assert( tr_isTorrent( tor ) );
295
296    if( tor == currentNode.torrent )
297    {
298        stopCurrent = TRUE;
299        while( stopCurrent )
300        {
301            tr_lockUnlock( lock );
302            tr_wait( 100 );
303            tr_lockLock( lock );
304        }
305    }
306    else
307    {
308        tr_free( tr_list_remove( &verifyList, tor, compareVerifyByTorrent ) );
309        tor->verifyState = TR_VERIFY_NONE;
310    }
311
312    tr_lockUnlock( lock );
313}
314
315void
316tr_verifyClose( tr_session * session UNUSED )
317{
318    tr_lockLock( getVerifyLock( ) );
319
320    stopCurrent = TRUE;
321    tr_list_free( &verifyList, tr_free );
322
323    tr_lockUnlock( getVerifyLock( ) );
324}
Note: See TracBrowser for help on using the repository browser.