source: trunk/libtransmission/verify.c @ 8161

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

(trunk libT) faster code for verifying entire torrents. Timing tests @ http://transmission.pastebin.com/m4e1d6ee

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