source: trunk/libtransmission/verify.c @ 11398

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

(trunk libT) add some new bugs to the code so that it will crash when vraa tries to use it

  • Property svn:keywords set to Date Rev Author Id
File size: 10.6 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 11398 2010-11-11 15:31:11Z 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 > 0 ) {
104                bytesThisPass = (uint32_t)numRead;
105                SHA1_Update( &sha, buffer, bytesThisPass );
106#if defined HAVE_POSIX_FADVISE && defined POSIX_FADV_DONTNEED
107                posix_fadvise( fd, filePos, bytesThisPass, POSIX_FADV_DONTNEED );
108#endif
109            }
110        }
111
112        /* move our offsets */
113        leftInPiece -= bytesThisPass;
114        leftInFile -= bytesThisPass;
115        piecePos += bytesThisPass;
116        filePos += bytesThisPass;
117
118        /* if we're finishing a piece... */
119        if( leftInPiece == 0 )
120        {
121            time_t now;
122            tr_bool hasPiece;
123            uint8_t hash[SHA_DIGEST_LENGTH];
124
125            SHA1_Final( hash, &sha );
126            hasPiece = !memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
127            /* fprintf( stderr, "do the hashes match? %s\n", (hasPiece?"yes":"no") ); */
128
129            if( hasPiece ) {
130                tr_torrentSetHasPiece( tor, pieceIndex, TRUE );
131                if( !hadPiece )
132                    changed = TRUE;
133            } else if( hadPiece ) {
134                tr_torrentSetHasPiece( tor, pieceIndex, FALSE );
135                changed = TRUE;
136            }
137            tr_torrentSetPieceChecked( tor, pieceIndex, TRUE );
138            now = tr_time( );
139            tor->anyDate = now;
140
141            /* sleeping even just a few msec per second goes a long
142             * way towards reducing IO load... */
143            if( lastSleptAt != now ) {
144                lastSleptAt = now;
145                tr_wait_msec( MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY );
146            }
147
148            SHA1_Init( &sha );
149            ++pieceIndex;
150            piecePos = 0;
151        }
152
153        /* if we're finishing a file... */
154        if( leftInFile == 0 )
155        {
156            /* fprintf( stderr, "closing file\n" ); */
157            if( fd >= 0 ) { tr_close_file( fd ); fd = -1; }
158            ++fileIndex;
159            filePos = 0;
160        }
161    }
162
163    /* cleanup */
164    if( fd >= 0 )
165        tr_close_file( fd );
166    free( buffer );
167
168    /* stopwatch */
169    end = tr_time( );
170    tr_tordbg( tor, "it took %d seconds to verify %"PRIu64" bytes (%"PRIu64" bytes per second)",
171               (int)(end-begin), tor->info.totalSize, (uint64_t)(tor->info.totalSize/(1+(end-begin))) );
172
173    return changed;
174}
175
176/***
177****
178***/
179
180struct verify_node
181{
182    tr_torrent *         torrent;
183    tr_verify_done_cb    verify_done_cb;
184    uint64_t             current_size;
185};
186
187static void
188fireCheckDone( tr_torrent * tor, tr_verify_done_cb verify_done_cb )
189{
190    assert( tr_isTorrent( tor ) );
191
192    if( verify_done_cb )
193        verify_done_cb( tor );
194}
195
196static struct verify_node currentNode;
197static tr_list * verifyList = NULL;
198static tr_thread * verifyThread = NULL;
199static tr_bool stopCurrent = FALSE;
200
201static tr_lock*
202getVerifyLock( void )
203{
204    static tr_lock * lock = NULL;
205
206    if( lock == NULL )
207        lock = tr_lockNew( );
208    return lock;
209}
210
211static void
212verifyThreadFunc( void * unused UNUSED )
213{
214    for( ;; )
215    {
216        int                  changed = 0;
217        tr_torrent         * tor;
218        struct verify_node * node;
219
220        tr_lockLock( getVerifyLock( ) );
221        stopCurrent = FALSE;
222        node = (struct verify_node*) verifyList ? verifyList->data : NULL;
223        if( node == NULL )
224        {
225            currentNode.torrent = NULL;
226            break;
227        }
228
229        currentNode = *node;
230        tor = currentNode.torrent;
231        tr_list_remove_data( &verifyList, node );
232        tr_free( node );
233        tr_lockUnlock( getVerifyLock( ) );
234
235        tr_torinf( tor, "%s", _( "Verifying torrent" ) );
236        tr_torrentSetVerifyState( tor, TR_VERIFY_NOW );
237        changed = verifyTorrent( tor, &stopCurrent );
238        tr_torrentSetVerifyState( tor, TR_VERIFY_NONE );
239        assert( tr_isTorrent( tor ) );
240
241        if( !stopCurrent )
242        {
243            if( changed )
244                tr_torrentSetDirty( tor );
245            fireCheckDone( tor, currentNode.verify_done_cb );
246        }
247    }
248
249    verifyThread = NULL;
250    tr_lockUnlock( getVerifyLock( ) );
251}
252
253static uint64_t
254getCurrentSize( tr_torrent * tor )
255{
256    tr_file_index_t i;
257    uint64_t byte_count = 0;
258    const tr_file_index_t n = tor->info.fileCount;
259
260    for( i=0; i<n; ++i )
261    {
262        struct stat sb;
263        char * filename = tr_torrentFindFile( tor, i );
264
265        sb.st_size = 0;
266        if( filename && !stat( filename, &sb ) )
267            byte_count += sb.st_size;
268
269        tr_free( filename );
270    }
271
272    return byte_count;
273}
274
275static int
276compareVerifyByPriorityAndSize( const void * va, const void * vb )
277{
278    const struct verify_node * a = va;
279    const struct verify_node * b = vb;
280
281    /* higher priority comes before lower priority */
282    const tr_priority_t pa =  tr_torrentGetPriority( a->torrent );
283    const tr_priority_t pb =  tr_torrentGetPriority( b->torrent );
284    if( pa != pb )
285        return pa > pb ? -1 : 1;
286
287    /* smaller size comes before larger size... smaller sizes are faster to verify */
288    if( a->current_size < b->current_size ) return -1;
289    if( a->current_size > b->current_size ) return  1;
290    return 0;
291}
292
293void
294tr_verifyAdd( tr_torrent *      tor,
295              tr_verify_done_cb verify_done_cb )
296{
297    assert( tr_isTorrent( tor ) );
298
299    if( tr_torrentCountUncheckedPieces( tor ) == 0 )
300    {
301        /* doesn't need to be checked... */
302        fireCheckDone( tor, verify_done_cb );
303    }
304    else
305    {
306        const uint64_t current_size = getCurrentSize( tor );
307
308        if( !current_size )
309        {
310            /* we haven't downloaded anything for this torrent yet...
311             * no need to leave it waiting in the back of the queue.
312             * we can mark it as all-missing from here and fire
313             * the "done" callback */
314            const tr_bool hadAny = tr_cpHaveTotal( &tor->completion ) != 0;
315            tr_piece_index_t i;
316            for( i=0; i<tor->info.pieceCount; ++i ) {
317                tr_torrentSetHasPiece( tor, i, FALSE );
318                tr_torrentSetPieceChecked( tor, i, TRUE );
319            }
320            if( hadAny ) /* if we thought we had some, flag as dirty */
321                tr_torrentSetDirty( tor );
322            fireCheckDone( tor, verify_done_cb );
323        }
324        else
325        {
326            struct verify_node * node;
327
328            tr_torinf( tor, "%s", _( "Queued for verification" ) );
329
330            node = tr_new( struct verify_node, 1 );
331            node->torrent = tor;
332            node->verify_done_cb = verify_done_cb;
333            node->current_size = current_size;
334
335            tr_lockLock( getVerifyLock( ) );
336            tr_torrentSetVerifyState( tor, TR_VERIFY_WAIT );
337            tr_list_insert_sorted( &verifyList, node, compareVerifyByPriorityAndSize );
338            if( verifyThread == NULL )
339                verifyThread = tr_threadNew( verifyThreadFunc, NULL );
340            tr_lockUnlock( getVerifyLock( ) );
341        }
342    }
343}
344
345static int
346compareVerifyByTorrent( const void * va, const void * vb )
347{
348    const struct verify_node * a = va;
349    const tr_torrent * b = vb;
350    return a->torrent - b;
351}
352
353void
354tr_verifyRemove( tr_torrent * tor )
355{
356    tr_lock * lock = getVerifyLock( );
357    tr_lockLock( lock );
358
359    assert( tr_isTorrent( tor ) );
360
361    if( tor == currentNode.torrent )
362    {
363        stopCurrent = TRUE;
364        while( stopCurrent )
365        {
366            tr_lockUnlock( lock );
367            tr_wait_msec( 100 );
368            tr_lockLock( lock );
369        }
370    }
371    else
372    {
373        tr_free( tr_list_remove( &verifyList, tor, compareVerifyByTorrent ) );
374        tr_torrentSetVerifyState( tor, TR_VERIFY_NONE );
375    }
376
377    tr_lockUnlock( lock );
378}
379
380void
381tr_verifyClose( tr_session * session UNUSED )
382{
383    tr_lockLock( getVerifyLock( ) );
384
385    stopCurrent = TRUE;
386    tr_list_free( &verifyList, tr_free );
387
388    tr_lockUnlock( getVerifyLock( ) );
389}
Note: See TracBrowser for help on using the repository browser.