source: trunk/libtransmission/verify.c @ 12177

Last change on this file since 12177 was 12177, checked in by jordan, 11 years ago

(trunk) it's bad form to #include so many system headers in libtransmission/utils.h...

  • Property svn:keywords set to Date Rev Author Id
File size: 8.5 KB
Line 
1/*
2 * This file Copyright (C) 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 12177 2011-03-16 18:04:23Z jordan $
11 */
12
13#include <string.h> /* memcmp() */
14#include <stdlib.h> /* free() */
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 "list.h"
27#include "platform.h" /* tr_lock() */
28#include "torrent.h"
29#include "utils.h" /* tr_valloc(), tr_free() */
30#include "verify.h"
31
32/***
33****
34***/
35
36enum
37{
38    MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY = 100
39};
40
41static tr_bool
42verifyTorrent( tr_torrent * tor, tr_bool * stopFlag )
43{
44    time_t end;
45    SHA_CTX sha;
46    int fd = -1;
47    int64_t filePos = 0;
48    tr_bool changed = 0;
49    tr_bool hadPiece = 0;
50    time_t lastSleptAt = 0;
51    uint32_t piecePos = 0;
52    tr_file_index_t fileIndex = 0;
53    tr_file_index_t prevFileIndex = !fileIndex;
54    tr_piece_index_t pieceIndex = 0;
55    const time_t begin = tr_time( );
56    const size_t buflen = 1024 * 128; /* 128 KiB buffer */
57    uint8_t * buffer = tr_valloc( buflen );
58
59    SHA1_Init( &sha );
60
61    tr_tordbg( tor, "%s", "verifying torrent..." );
62    tr_torrentSetChecked( tor, 0 );
63    while( !*stopFlag && ( pieceIndex < tor->info.pieceCount ) )
64    {
65        uint32_t leftInPiece;
66        uint32_t bytesThisPass;
67        uint64_t leftInFile;
68        const tr_file * file = &tor->info.files[fileIndex];
69
70        /* if we're starting a new piece... */
71        if( piecePos == 0 )
72            hadPiece = tr_cpPieceIsComplete( &tor->completion, pieceIndex );
73
74        /* if we're starting a new file... */
75        if( !filePos && (fd<0) && (fileIndex!=prevFileIndex) )
76        {
77            char * filename = tr_torrentFindFile( tor, fileIndex );
78            fd = filename == NULL ? -1 : tr_open_file_for_scanning( filename );
79            tr_free( filename );
80            prevFileIndex = fileIndex;
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
89        /* read a bit */
90        if( fd >= 0 ) {
91            const ssize_t numRead = tr_pread( fd, buffer, bytesThisPass, filePos );
92            if( numRead > 0 ) {
93                bytesThisPass = (uint32_t)numRead;
94                SHA1_Update( &sha, buffer, bytesThisPass );
95#if defined HAVE_POSIX_FADVISE && defined POSIX_FADV_DONTNEED
96                posix_fadvise( fd, filePos, bytesThisPass, POSIX_FADV_DONTNEED );
97#endif
98            }
99        }
100
101        /* move our offsets */
102        leftInPiece -= bytesThisPass;
103        leftInFile -= bytesThisPass;
104        piecePos += bytesThisPass;
105        filePos += bytesThisPass;
106
107        /* if we're finishing a piece... */
108        if( leftInPiece == 0 )
109        {
110            time_t now;
111            tr_bool hasPiece;
112            uint8_t hash[SHA_DIGEST_LENGTH];
113
114            SHA1_Final( hash, &sha );
115            hasPiece = !memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
116
117            if( hasPiece || hadPiece ) {
118                tr_torrentSetHasPiece( tor, pieceIndex, hasPiece );
119                changed |= hasPiece != hadPiece;
120            }
121            tr_torrentSetPieceChecked( tor, pieceIndex );
122            now = tr_time( );
123            tor->anyDate = now;
124
125            /* sleeping even just a few msec per second goes a long
126             * way towards reducing IO load... */
127            if( lastSleptAt != now ) {
128                lastSleptAt = now;
129                tr_wait_msec( MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY );
130            }
131
132            SHA1_Init( &sha );
133            ++pieceIndex;
134            piecePos = 0;
135        }
136
137        /* if we're finishing a file... */
138        if( leftInFile == 0 )
139        {
140            if( fd >= 0 ) { tr_close_file( fd ); fd = -1; }
141            ++fileIndex;
142            filePos = 0;
143        }
144    }
145
146    /* cleanup */
147    if( fd >= 0 )
148        tr_close_file( fd );
149    free( buffer );
150
151    /* stopwatch */
152    end = tr_time( );
153    tr_tordbg( tor, "Verification is done. It took %d seconds to verify %"PRIu64" bytes (%"PRIu64" bytes per second)",
154               (int)(end-begin), tor->info.totalSize,
155               (uint64_t)(tor->info.totalSize/(1+(end-begin))) );
156
157    return changed;
158}
159
160/***
161****
162***/
163
164struct verify_node
165{
166    tr_torrent *         torrent;
167    tr_verify_done_cb    verify_done_cb;
168    uint64_t             current_size;
169};
170
171static void
172fireCheckDone( tr_torrent * tor, tr_verify_done_cb verify_done_cb )
173{
174    assert( tr_isTorrent( tor ) );
175
176    if( verify_done_cb )
177        verify_done_cb( tor );
178}
179
180static struct verify_node currentNode;
181static tr_list * verifyList = NULL;
182static tr_thread * verifyThread = NULL;
183static tr_bool stopCurrent = FALSE;
184
185static tr_lock*
186getVerifyLock( void )
187{
188    static tr_lock * lock = NULL;
189    if( lock == NULL )
190        lock = tr_lockNew( );
191    return lock;
192}
193
194static void
195verifyThreadFunc( void * unused UNUSED )
196{
197    for( ;; )
198    {
199        int changed = 0;
200        tr_torrent * tor;
201        struct verify_node * node;
202
203        tr_lockLock( getVerifyLock( ) );
204        stopCurrent = FALSE;
205        node = (struct verify_node*) verifyList ? verifyList->data : NULL;
206        if( node == NULL )
207        {
208            currentNode.torrent = NULL;
209            break;
210        }
211
212        currentNode = *node;
213        tor = currentNode.torrent;
214        tr_list_remove_data( &verifyList, node );
215        tr_free( node );
216        tr_lockUnlock( getVerifyLock( ) );
217
218        tr_torinf( tor, "%s", _( "Verifying torrent" ) );
219        tr_torrentSetVerifyState( tor, TR_VERIFY_NOW );
220        changed = verifyTorrent( tor, &stopCurrent );
221        tr_torrentSetVerifyState( tor, TR_VERIFY_NONE );
222        assert( tr_isTorrent( tor ) );
223
224        if( !stopCurrent )
225        {
226            if( changed )
227                tr_torrentSetDirty( tor );
228            fireCheckDone( tor, currentNode.verify_done_cb );
229        }
230    }
231
232    verifyThread = NULL;
233    tr_lockUnlock( getVerifyLock( ) );
234}
235
236static int
237compareVerifyByPriorityAndSize( const void * va, const void * vb )
238{
239    const struct verify_node * a = va;
240    const struct verify_node * b = vb;
241
242    /* higher priority comes before lower priority */
243    const tr_priority_t pa = tr_torrentGetPriority( a->torrent );
244    const tr_priority_t pb = tr_torrentGetPriority( b->torrent );
245    if( pa != pb )
246        return pa > pb ? -1 : 1;
247
248    /* smaller torrents come before larger ones because they verify faster */
249    if( a->current_size < b->current_size ) return -1;
250    if( a->current_size > b->current_size ) return  1;
251    return 0;
252}
253
254void
255tr_verifyAdd( tr_torrent * tor, tr_verify_done_cb verify_done_cb )
256{
257    struct verify_node * node;
258
259    assert( tr_isTorrent( tor ) );
260    tr_torinf( tor, "%s", _( "Queued for verification" ) );
261
262    node = tr_new( struct verify_node, 1 );
263    node->torrent = tor;
264    node->verify_done_cb = verify_done_cb;
265    node->current_size = tr_torrentGetCurrentSizeOnDisk( tor );
266
267    tr_lockLock( getVerifyLock( ) );
268    tr_torrentSetVerifyState( tor, TR_VERIFY_WAIT );
269    tr_list_insert_sorted( &verifyList, node, compareVerifyByPriorityAndSize );
270    if( verifyThread == NULL )
271        verifyThread = tr_threadNew( verifyThreadFunc, NULL );
272    tr_lockUnlock( getVerifyLock( ) );
273}
274
275static int
276compareVerifyByTorrent( const void * va, const void * vb )
277{
278    const struct verify_node * a = va;
279    const tr_torrent * b = vb;
280    return a->torrent - b;
281}
282
283void
284tr_verifyRemove( tr_torrent * tor )
285{
286    tr_lock * lock = getVerifyLock( );
287    tr_lockLock( lock );
288
289    assert( tr_isTorrent( tor ) );
290
291    if( tor == currentNode.torrent )
292    {
293        stopCurrent = TRUE;
294        while( stopCurrent )
295        {
296            tr_lockUnlock( lock );
297            tr_wait_msec( 100 );
298            tr_lockLock( lock );
299        }
300    }
301    else
302    {
303        tr_free( tr_list_remove( &verifyList, tor, compareVerifyByTorrent ) );
304        tr_torrentSetVerifyState( tor, TR_VERIFY_NONE );
305    }
306
307    tr_lockUnlock( lock );
308}
309
310void
311tr_verifyClose( tr_session * session UNUSED )
312{
313    tr_lockLock( getVerifyLock( ) );
314
315    stopCurrent = TRUE;
316    tr_list_free( &verifyList, tr_free );
317
318    tr_lockUnlock( getVerifyLock( ) );
319}
Note: See TracBrowser for help on using the repository browser.