source: trunk/libtransmission/verify.c @ 11709

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

Update the copyright year in the source code comments.

The Berne Convention says that the copyright year is moot, so instead of adding another year to each file as in previous years, I've removed the year altogether from the source code comments in libtransmission, gtk, qt, utils, daemon, and cli.

Juliusz's copyright notice in tr-dht and Johannes' copyright notice in tr-lpd have been left alone; it didn't seem appropriate to modify them.

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