2 * This file Copyright (C) Mnemosyne LLC
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.
10 * $Id: verify.c 11709 2011-01-19 13:48:47Z jordan $
13 #ifdef HAVE_POSIX_FADVISE
14 #define _XOPEN_SOURCE 600
15 #include <fcntl.h> /* posix_fadvise() */
18 #include <openssl/sha.h>
20 #include "transmission.h"
21 #include "completion.h"
24 #include "platform.h" /* tr_lock() */
26 #include "utils.h" /* tr_valloc(), tr_free() */
35 MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY
= 100
39 verifyTorrent( tr_torrent
* tor
, tr_bool
* stopFlag
)
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
);
58 tr_tordbg( tor
, "%s", "verifying torrent..." );
59 tr_torrentSetChecked( tor
, 0 );
60 while( !*stopFlag
&& ( pieceIndex
< tor
->info
.pieceCount
) )
63 uint32_t bytesThisPass
;
65 const tr_file
* file
= &tor
->info
.files
[fileIndex
];
67 /* if we're starting a new piece... */
69 hadPiece
= tr_cpPieceIsComplete( &tor
->completion
, pieceIndex
);
71 /* if we're starting a new file... */
72 if( !filePos
&& (fd
<0) && (fileIndex
!=prevFileIndex
) )
74 char * filename
= tr_torrentFindFile( tor
, fileIndex
);
75 fd
= filename
== NULL
? -1 : tr_open_file_for_scanning( filename
);
77 prevFileIndex
= fileIndex
;
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
);
88 const ssize_t numRead
= tr_pread( fd
, buffer
, bytesThisPass
, filePos
);
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
);
98 /* move our offsets */
99 leftInPiece
-= bytesThisPass
;
100 leftInFile
-= bytesThisPass
;
101 piecePos
+= bytesThisPass
;
102 filePos
+= bytesThisPass
;
104 /* if we're finishing a piece... */
105 if( leftInPiece
== 0 )
109 uint8_t hash
[SHA_DIGEST_LENGTH
];
111 SHA1_Final( hash
, &sha
);
112 hasPiece
= !memcmp( hash
, tor
->info
.pieces
[pieceIndex
].hash
, SHA_DIGEST_LENGTH
);
114 if( hasPiece
|| hadPiece
) {
115 tr_torrentSetHasPiece( tor
, pieceIndex
, hasPiece
);
116 changed
|= hasPiece
!= hadPiece
;
118 tr_torrentSetPieceChecked( tor
, pieceIndex
);
122 /* sleeping even just a few msec per second goes a long
123 * way towards reducing IO load... */
124 if( lastSleptAt
!= now
) {
126 tr_wait_msec( MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY
);
134 /* if we're finishing a file... */
135 if( leftInFile
== 0 )
137 if( fd
>= 0 ) { tr_close_file( fd
); fd
= -1; }
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
))) );
163 tr_torrent
* torrent
;
164 tr_verify_done_cb verify_done_cb
;
165 uint64_t current_size
;
169 fireCheckDone( tr_torrent
* tor
, tr_verify_done_cb verify_done_cb
)
171 assert( tr_isTorrent( tor
) );
174 verify_done_cb( tor
);
177 static struct verify_node currentNode
;
178 static tr_list
* verifyList
= NULL
;
179 static tr_thread
* verifyThread
= NULL
;
180 static tr_bool stopCurrent
= FALSE
;
183 getVerifyLock( void )
185 static tr_lock
* lock
= NULL
;
187 lock
= tr_lockNew( );
192 verifyThreadFunc( void * unused UNUSED
)
198 struct verify_node
* node
;
200 tr_lockLock( getVerifyLock( ) );
202 node
= (struct verify_node
*) verifyList
? verifyList
->data
: NULL
;
205 currentNode
.torrent
= NULL
;
210 tor
= currentNode
.torrent
;
211 tr_list_remove_data( &verifyList
, node
);
213 tr_lockUnlock( getVerifyLock( ) );
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
) );
224 tr_torrentSetDirty( tor
);
225 fireCheckDone( tor
, currentNode
.verify_done_cb
);
230 tr_lockUnlock( getVerifyLock( ) );
234 compareVerifyByPriorityAndSize( const void * va
, const void * vb
)
236 const struct verify_node
* a
= va
;
237 const struct verify_node
* b
= vb
;
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
);
243 return pa
> pb
? -1 : 1;
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;
252 tr_verifyAdd( tr_torrent
* tor
, tr_verify_done_cb verify_done_cb
)
254 struct verify_node
* node
;
256 assert( tr_isTorrent( tor
) );
257 tr_torinf( tor
, "%s", _( "Queued for verification" ) );
259 node
= tr_new( struct verify_node
, 1 );
261 node
->verify_done_cb
= verify_done_cb
;
262 node
->current_size
= tr_torrentGetCurrentSizeOnDisk( tor
);
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( ) );
273 compareVerifyByTorrent( const void * va
, const void * vb
)
275 const struct verify_node
* a
= va
;
276 const tr_torrent
* b
= vb
;
277 return a
->torrent
- b
;
281 tr_verifyRemove( tr_torrent
* tor
)
283 tr_lock
* lock
= getVerifyLock( );
286 assert( tr_isTorrent( tor
) );
288 if( tor
== currentNode
.torrent
)
293 tr_lockUnlock( lock
);
300 tr_free( tr_list_remove( &verifyList
, tor
, compareVerifyByTorrent
) );
301 tr_torrentSetVerifyState( tor
, TR_VERIFY_NONE
);
304 tr_lockUnlock( lock
);
308 tr_verifyClose( tr_session
* session UNUSED
)
310 tr_lockLock( getVerifyLock( ) );
313 tr_list_free( &verifyList
, tr_free
);
315 tr_lockUnlock( getVerifyLock( ) );