source: trunk/libtransmission/verify.c @ 11304

Last change on this file since 11304 was 11304, checked in by charles, 11 years ago

(trunk libT) remove dead variable pieceBytesRead from verify.c. Reported by Longinus00

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