source: trunk/libtransmission/verify.c @ 8171

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

(trunk libT) yet another fix found by RolCol?

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