source: trunk/libtransmission/verify.c @ 14241

Last change on this file since 14241 was 14241, checked in by jordan, 8 years ago

Copyedit the license's revised text: (1) remove unnecessary repitition use of the word 'license' from the top of the header and source files (2) add the standard 'we hope it's useful, but no warranty' clause to COPYING (3) make explicit that linking OpenSSL is allowed (see https://people.gnome.org/~markmc/openssl-and-the-gpl.html for background) (4) sync the Qt and GTK+ clients' license popups with COPYING's revised text

  • Property svn:keywords set to Date Rev Author Id
File size: 8.2 KB
Line 
1/*
2 * This file Copyright (C) 2007-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: verify.c 14241 2014-01-21 03:10:30Z jordan $
8 */
9
10#include <string.h> /* memcmp () */
11#include <stdlib.h> /* free () */
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 "log.h"
25#include "platform.h" /* tr_lock () */
26#include "torrent.h"
27#include "utils.h" /* tr_valloc (), tr_free () */
28#include "verify.h"
29
30/***
31****
32***/
33
34enum
35{
36  MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY = 100
37};
38
39static bool
40verifyTorrent (tr_torrent * tor, bool * stopFlag)
41{
42  time_t end;
43  SHA_CTX sha;
44  int fd = -1;
45  int64_t filePos = 0;
46  bool changed = 0;
47  bool hadPiece = 0;
48  time_t lastSleptAt = 0;
49  uint32_t piecePos = 0;
50  tr_file_index_t fileIndex = 0;
51  tr_file_index_t prevFileIndex = !fileIndex;
52  tr_piece_index_t pieceIndex = 0;
53  const time_t begin = tr_time ();
54  const size_t buflen = 1024 * 128; /* 128 KiB buffer */
55  uint8_t * buffer = tr_valloc (buflen);
56
57  SHA1_Init (&sha);
58
59  tr_logAddTorDbg (tor, "%s", "verifying torrent...");
60  tr_torrentSetChecked (tor, 0);
61  while (!*stopFlag && (pieceIndex < tor->info.pieceCount))
62    {
63      uint32_t leftInPiece;
64      uint32_t bytesThisPass;
65      uint64_t leftInFile;
66      const tr_file * file = &tor->info.files[fileIndex];
67
68      /* if we're starting a new piece... */
69      if (piecePos == 0)
70        hadPiece = tr_torrentPieceIsComplete (tor, pieceIndex);
71
72      /* if we're starting a new file... */
73      if (!filePos && (fd<0) && (fileIndex!=prevFileIndex))
74        {
75          char * filename = tr_torrentFindFile (tor, fileIndex);
76          fd = filename == NULL ? -1 : tr_open_file_for_scanning (filename);
77          tr_free (filename);
78          prevFileIndex = fileIndex;
79        }
80
81      /* figure out how much we can read this pass */
82      leftInPiece = tr_torPieceCountBytes (tor, pieceIndex) - piecePos;
83      leftInFile = file->length - filePos;
84      bytesThisPass = MIN (leftInFile, leftInPiece);
85      bytesThisPass = MIN (bytesThisPass, buflen);
86
87      /* read a bit */
88      if (fd >= 0)
89        {
90          const ssize_t numRead = tr_pread (fd, buffer, bytesThisPass, filePos);
91          if (numRead > 0)
92            {
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          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            {
119              tr_torrentSetHasPiece (tor, pieceIndex, hasPiece);
120              changed |= hasPiece != hadPiece;
121            }
122
123          tr_torrentSetPieceChecked (tor, pieceIndex);
124          now = tr_time ();
125          tor->anyDate = now;
126
127          /* sleeping even just a few msec per second goes a long
128           * way towards reducing IO load... */
129          if (lastSleptAt != now)
130            {
131              lastSleptAt = now;
132              tr_wait_msec (MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY);
133            }
134
135          SHA1_Init (&sha);
136          pieceIndex++;
137          piecePos = 0;
138        }
139
140      /* if we're finishing a file... */
141      if (leftInFile == 0)
142        {
143          if (fd >= 0)
144            {
145              tr_close_file (fd);
146              fd = -1;
147            }
148          fileIndex++;
149          filePos = 0;
150        }
151    }
152
153  /* cleanup */
154  if (fd >= 0)
155    tr_close_file (fd);
156  free (buffer);
157
158  /* stopwatch */
159  end = tr_time ();
160  tr_logAddTorDbg (tor, "Verification is done. It took %d seconds to verify %"PRIu64" bytes (%"PRIu64" bytes per second)",
161             (int)(end-begin), tor->info.totalSize,
162             (uint64_t)(tor->info.totalSize/ (1+ (end-begin))));
163
164  return changed;
165}
166
167/***
168****
169***/
170
171struct verify_node
172{
173  tr_torrent          * torrent;
174  tr_verify_done_func   callback_func;
175  void                * callback_data;
176  uint64_t              current_size;
177};
178
179static struct verify_node currentNode;
180static tr_list * verifyList = NULL;
181static tr_thread * verifyThread = NULL;
182static bool stopCurrent = false;
183
184static tr_lock*
185getVerifyLock (void)
186{
187  static tr_lock * lock = NULL;
188
189  if (lock == NULL)
190    lock = tr_lockNew ();
191
192  return lock;
193}
194
195static void
196verifyThreadFunc (void * unused UNUSED)
197{
198  for (;;)
199    {
200      int changed = 0;
201      tr_torrent * tor;
202      struct verify_node * node;
203
204      tr_lockLock (getVerifyLock ());
205      stopCurrent = false;
206      node = (struct verify_node*) verifyList ? verifyList->data : NULL;
207      if (node == NULL)
208        {
209          currentNode.torrent = NULL;
210          break;
211        }
212
213      currentNode = *node;
214      tor = currentNode.torrent;
215      tr_list_remove_data (&verifyList, node);
216      tr_free (node);
217      tr_lockUnlock (getVerifyLock ());
218
219      tr_logAddTorInfo (tor, "%s", _("Verifying torrent"));
220      tr_torrentSetVerifyState (tor, TR_VERIFY_NOW);
221      changed = verifyTorrent (tor, &stopCurrent);
222      tr_torrentSetVerifyState (tor, TR_VERIFY_NONE);
223      assert (tr_isTorrent (tor));
224
225      if (!stopCurrent && changed)
226        tr_torrentSetDirty (tor);
227
228      if (currentNode.callback_func)
229        (*currentNode.callback_func)(tor, stopCurrent, currentNode.callback_data);
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)
250    return -1;
251  if (a->current_size > b->current_size)
252    return  1;
253  return 0;
254}
255
256void
257tr_verifyAdd (tr_torrent           * tor,
258              tr_verify_done_func    callback_func,
259              void                 * callback_data)
260{
261  struct verify_node * node;
262
263  assert (tr_isTorrent (tor));
264  tr_logAddTorInfo (tor, "%s", _("Queued for verification"));
265
266  node = tr_new (struct verify_node, 1);
267  node->torrent = tor;
268  node->callback_func = callback_func;
269  node->callback_data = callback_data;
270  node->current_size = tr_torrentGetCurrentSizeOnDisk (tor);
271
272  tr_lockLock (getVerifyLock ());
273  tr_torrentSetVerifyState (tor, TR_VERIFY_WAIT);
274  tr_list_insert_sorted (&verifyList, node, compareVerifyByPriorityAndSize);
275  if (verifyThread == NULL)
276    verifyThread = tr_threadNew (verifyThreadFunc, NULL);
277  tr_lockUnlock (getVerifyLock ());
278}
279
280static int
281compareVerifyByTorrent (const void * va, const void * vb)
282{
283  const struct verify_node * a = va;
284  const tr_torrent * b = vb;
285  return a->torrent - b;
286}
287
288void
289tr_verifyRemove (tr_torrent * tor)
290{
291  tr_lock * lock = getVerifyLock ();
292  tr_lockLock (lock);
293
294  assert (tr_isTorrent (tor));
295
296  if (tor == currentNode.torrent)
297    {
298      stopCurrent = true;
299
300      while (stopCurrent)
301        {
302          tr_lockUnlock (lock);
303          tr_wait_msec (100);
304          tr_lockLock (lock);
305        }
306    }
307  else
308    {
309      struct verify_node * node = tr_list_remove (&verifyList, tor, compareVerifyByTorrent);
310
311      tr_torrentSetVerifyState (tor, TR_VERIFY_NONE);
312
313      if (node != NULL)
314        {
315          if (node->callback_func != NULL)
316            (*node->callback_func)(tor, true, node->callback_data);
317
318          tr_free (node);
319        }
320    }
321
322  tr_lockUnlock (lock);
323}
324
325void
326tr_verifyClose (tr_session * session UNUSED)
327{
328  tr_lockLock (getVerifyLock ());
329
330  stopCurrent = true;
331  tr_list_free (&verifyList, tr_free);
332
333  tr_lockUnlock (getVerifyLock ());
334}
335
Note: See TracBrowser for help on using the repository browser.