transmission: update from 2.13 to 2.22
[tomato.git] / release / src / router / transmission / libtransmission / verify.c
blob3646c722bc6db2db45573439e962ce1695f0efb2
1 /*
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() */
16 #endif
18 #include <openssl/sha.h>
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"
29 /***
30 ****
31 ***/
33 enum
35 MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY = 100
38 static tr_bool
39 verifyTorrent( tr_torrent * tor, tr_bool * stopFlag )
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 );
56 SHA1_Init( &sha );
58 tr_tordbg( tor, "%s", "verifying torrent..." );
59 tr_torrentSetChecked( tor, 0 );
60 while( !*stopFlag && ( pieceIndex < tor->info.pieceCount ) )
62 uint32_t leftInPiece;
63 uint32_t bytesThisPass;
64 uint64_t leftInFile;
65 const tr_file * file = &tor->info.files[fileIndex];
67 /* if we're starting a new piece... */
68 if( piecePos == 0 )
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 );
76 tr_free( 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 );
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
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 )
107 time_t now;
108 tr_bool hasPiece;
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 );
119 now = tr_time( );
120 tor->anyDate = now;
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 );
129 SHA1_Init( &sha );
130 ++pieceIndex;
131 piecePos = 0;
134 /* if we're finishing a file... */
135 if( leftInFile == 0 )
137 if( fd >= 0 ) { tr_close_file( fd ); fd = -1; }
138 ++fileIndex;
139 filePos = 0;
143 /* cleanup */
144 if( fd >= 0 )
145 tr_close_file( fd );
146 free( buffer );
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))) );
154 return changed;
157 /***
158 ****
159 ***/
161 struct verify_node
163 tr_torrent * torrent;
164 tr_verify_done_cb verify_done_cb;
165 uint64_t current_size;
168 static void
169 fireCheckDone( tr_torrent * tor, tr_verify_done_cb verify_done_cb )
171 assert( tr_isTorrent( tor ) );
173 if( verify_done_cb )
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;
182 static tr_lock*
183 getVerifyLock( void )
185 static tr_lock * lock = NULL;
186 if( lock == NULL )
187 lock = tr_lockNew( );
188 return lock;
191 static void
192 verifyThreadFunc( void * unused UNUSED )
194 for( ;; )
196 int changed = 0;
197 tr_torrent * tor;
198 struct verify_node * node;
200 tr_lockLock( getVerifyLock( ) );
201 stopCurrent = FALSE;
202 node = (struct verify_node*) verifyList ? verifyList->data : NULL;
203 if( node == NULL )
205 currentNode.torrent = NULL;
206 break;
209 currentNode = *node;
210 tor = currentNode.torrent;
211 tr_list_remove_data( &verifyList, node );
212 tr_free( 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 ) );
221 if( !stopCurrent )
223 if( changed )
224 tr_torrentSetDirty( tor );
225 fireCheckDone( tor, currentNode.verify_done_cb );
229 verifyThread = NULL;
230 tr_lockUnlock( getVerifyLock( ) );
233 static int
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 );
242 if( pa != pb )
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;
248 return 0;
251 void
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 );
260 node->torrent = tor;
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( ) );
272 static int
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;
280 void
281 tr_verifyRemove( tr_torrent * tor )
283 tr_lock * lock = getVerifyLock( );
284 tr_lockLock( lock );
286 assert( tr_isTorrent( tor ) );
288 if( tor == currentNode.torrent )
290 stopCurrent = TRUE;
291 while( stopCurrent )
293 tr_lockUnlock( lock );
294 tr_wait_msec( 100 );
295 tr_lockLock( lock );
298 else
300 tr_free( tr_list_remove( &verifyList, tor, compareVerifyByTorrent ) );
301 tr_torrentSetVerifyState( tor, TR_VERIFY_NONE );
304 tr_lockUnlock( lock );
307 void
308 tr_verifyClose( tr_session * session UNUSED )
310 tr_lockLock( getVerifyLock( ) );
312 stopCurrent = TRUE;
313 tr_list_free( &verifyList, tr_free );
315 tr_lockUnlock( getVerifyLock( ) );