source: trunk/libtransmission/verify.c @ 8175

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

(trunk libT) #1976: build problem with r8173: "erreur: ‘POSIX_FADV_SEQUENTIAL’ undeclared"

  • Property svn:keywords set to Date Rev Author Id
File size: 8.0 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 8175 2009-04-07 20:25:32Z 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_reading( filename, TRUE );
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 ) { fclose( fp ); fp = NULL; }
133            ++fileIndex;
134            filePos = 0;
135        }
136    }
137
138    /* cleanup */
139    if( fp != NULL )
140        fclose( 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
272int
273tr_verifyInProgress( const tr_torrent * tor )
274{
275    int 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
Note: See TracBrowser for help on using the repository browser.