source: trunk/libtransmission/verify.c @ 13631

Last change on this file since 13631 was 13631, checked in by jordan, 9 years ago

(trunk, libT) #5165: fix r13625 oops

  • Property svn:keywords set to Date Rev Author Id
File size: 8.1 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 13631 2012-12-07 01:53:31Z 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 bool
42verifyTorrent (tr_torrent * tor, bool * stopFlag)
43{
44  time_t end;
45  SHA_CTX sha;
46  int fd = -1;
47  int64_t filePos = 0;
48  bool changed = 0;
49  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        {
92          const ssize_t numRead = tr_pread (fd, buffer, bytesThisPass, filePos);
93          if (numRead > 0)
94            {
95              bytesThisPass = (uint32_t)numRead;
96              SHA1_Update (&sha, buffer, bytesThisPass);
97#if defined HAVE_POSIX_FADVISE && defined POSIX_FADV_DONTNEED
98              posix_fadvise (fd, filePos, bytesThisPass, POSIX_FADV_DONTNEED);
99#endif
100            }
101        }
102
103      /* move our offsets */
104      leftInPiece -= bytesThisPass;
105      leftInFile -= bytesThisPass;
106      piecePos += bytesThisPass;
107      filePos += bytesThisPass;
108
109      /* if we're finishing a piece... */
110      if (leftInPiece == 0)
111        {
112          time_t now;
113          bool hasPiece;
114          uint8_t hash[SHA_DIGEST_LENGTH];
115
116          SHA1_Final (hash, &sha);
117          hasPiece = !memcmp (hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH);
118
119          if (hasPiece || hadPiece)
120            {
121              tr_torrentSetHasPiece (tor, pieceIndex, hasPiece);
122              changed |= hasPiece != hadPiece;
123            }
124
125          tr_torrentSetPieceChecked (tor, pieceIndex);
126          now = tr_time ();
127          tor->anyDate = now;
128
129          /* sleeping even just a few msec per second goes a long
130           * way towards reducing IO load... */
131          if (lastSleptAt != now)
132            {
133              lastSleptAt = now;
134              tr_wait_msec (MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY);
135            }
136
137          SHA1_Init (&sha);
138          pieceIndex++;
139          piecePos = 0;
140        }
141
142      /* if we're finishing a file... */
143      if (leftInFile == 0)
144        {
145          if (fd >= 0)
146            {
147              tr_close_file (fd);
148              fd = -1;
149            }
150          fileIndex++;
151          filePos = 0;
152        }
153    }
154
155  /* cleanup */
156  if (fd >= 0)
157    tr_close_file (fd);
158  free (buffer);
159
160  /* stopwatch */
161  end = tr_time ();
162  tr_tordbg (tor, "Verification is done. It took %d seconds to verify %"PRIu64" bytes (%"PRIu64" bytes per second)",
163             (int)(end-begin), tor->info.totalSize,
164             (uint64_t)(tor->info.totalSize/ (1+ (end-begin))));
165
166  return changed;
167}
168
169/***
170****
171***/
172
173struct verify_node
174{
175  tr_torrent *         torrent;
176  tr_verify_done_cb    verify_done_cb;
177  uint64_t             current_size;
178};
179
180static void
181fireCheckDone (tr_torrent * tor, tr_verify_done_cb verify_done_cb)
182{
183  assert (tr_isTorrent (tor));
184
185  if (verify_done_cb)
186    verify_done_cb (tor);
187}
188
189static struct verify_node currentNode;
190static tr_list * verifyList = NULL;
191static tr_thread * verifyThread = NULL;
192static bool stopCurrent = false;
193
194static tr_lock*
195getVerifyLock (void)
196{
197  static tr_lock * lock = NULL;
198
199  if (lock == NULL)
200    lock = tr_lockNew ();
201
202  return lock;
203}
204
205static void
206verifyThreadFunc (void * unused UNUSED)
207{
208  for (;;)
209    {
210      int changed = 0;
211      tr_torrent * tor;
212      struct verify_node * node;
213
214      tr_lockLock (getVerifyLock ());
215      stopCurrent = false;
216      node = (struct verify_node*) verifyList ? verifyList->data : NULL;
217      if (node == NULL)
218        {
219          currentNode.torrent = NULL;
220          break;
221        }
222
223      currentNode = *node;
224      tor = currentNode.torrent;
225      tr_list_remove_data (&verifyList, node);
226      tr_free (node);
227      tr_lockUnlock (getVerifyLock ());
228
229      tr_torinf (tor, "%s", _("Verifying torrent"));
230      tr_torrentSetVerifyState (tor, TR_VERIFY_NOW);
231      changed = verifyTorrent (tor, &stopCurrent);
232      tr_torrentSetVerifyState (tor, TR_VERIFY_NONE);
233      assert (tr_isTorrent (tor));
234
235      if (!stopCurrent)
236        {
237          if (changed)
238            tr_torrentSetDirty (tor);
239          fireCheckDone (tor, currentNode.verify_done_cb);
240        }
241    }
242
243  verifyThread = NULL;
244  tr_lockUnlock (getVerifyLock ());
245}
246
247static int
248compareVerifyByPriorityAndSize (const void * va, const void * vb)
249{
250  const struct verify_node * a = va;
251  const struct verify_node * b = vb;
252
253  /* higher priority comes before lower priority */
254  const tr_priority_t pa = tr_torrentGetPriority (a->torrent);
255  const tr_priority_t pb = tr_torrentGetPriority (b->torrent);
256  if (pa != pb)
257    return pa > pb ? -1 : 1;
258
259  /* smaller torrents come before larger ones because they verify faster */
260  if (a->current_size < b->current_size)
261    return -1;
262  if (a->current_size > b->current_size)
263    return  1;
264  return 0;
265}
266
267void
268tr_verifyAdd (tr_torrent * tor, tr_verify_done_cb verify_done_cb)
269{
270  struct verify_node * node;
271
272  assert (tr_isTorrent (tor));
273  tr_torinf (tor, "%s", _("Queued for verification"));
274
275  node = tr_new (struct verify_node, 1);
276  node->torrent = tor;
277  node->verify_done_cb = verify_done_cb;
278  node->current_size = tr_torrentGetCurrentSizeOnDisk (tor);
279
280  tr_lockLock (getVerifyLock ());
281  tr_torrentSetVerifyState (tor, TR_VERIFY_WAIT);
282  tr_list_insert_sorted (&verifyList, node, compareVerifyByPriorityAndSize);
283  if (verifyThread == NULL)
284    verifyThread = tr_threadNew (verifyThreadFunc, NULL);
285  tr_lockUnlock (getVerifyLock ());
286}
287
288static int
289compareVerifyByTorrent (const void * va, const void * vb)
290{
291  const struct verify_node * a = va;
292  const tr_torrent * b = vb;
293  return a->torrent - b;
294}
295
296void
297tr_verifyRemove (tr_torrent * tor)
298{
299  tr_lock * lock = getVerifyLock ();
300  tr_lockLock (lock);
301
302  assert (tr_isTorrent (tor));
303
304  if (tor == currentNode.torrent)
305    {
306      stopCurrent = true;
307
308      while (stopCurrent)
309        {
310          tr_lockUnlock (lock);
311          tr_wait_msec (100);
312          tr_lockLock (lock);
313        }
314    }
315  else
316    {
317      tr_free (tr_list_remove (&verifyList, tor, compareVerifyByTorrent));
318      tr_torrentSetVerifyState (tor, TR_VERIFY_NONE);
319    }
320
321  tr_lockUnlock (lock);
322}
323
324void
325tr_verifyClose (tr_session * session UNUSED)
326{
327  tr_lockLock (getVerifyLock ());
328
329  stopCurrent = true;
330  tr_list_free (&verifyList, tr_free);
331
332  tr_lockUnlock (getVerifyLock ());
333}
334
Note: See TracBrowser for help on using the repository browser.