source: trunk/libtransmission/verify.c

Last change on this file was 14718, checked in by mikedld, 5 years ago

Explicitly compare result of str(n)cmp/memcmp to signify that it's not boolean

  • Property svn:keywords set to Date Rev Author Id
File size: 8.6 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 14718 2016-03-13 22:11:01Z mikedld $
8 */
9
10#if defined (HAVE_POSIX_FADVISE) && (!defined (_XOPEN_SOURCE) || _XOPEN_SOURCE < 600)
11 #undef _XOPEN_SOURCE
12 #define _XOPEN_SOURCE 600
13#endif
14
15#include <string.h> /* memcmp () */
16#include <stdlib.h> /* free () */
17
18#ifdef HAVE_POSIX_FADVISE
19 #include <fcntl.h> /* posix_fadvise () */
20#endif
21
22#include "transmission.h"
23#include "completion.h"
24#include "crypto-utils.h"
25#include "file.h"
26#include "list.h"
27#include "log.h"
28#include "platform.h" /* tr_lock () */
29#include "torrent.h"
30#include "utils.h" /* tr_valloc (), tr_free () */
31#include "verify.h"
32
33/***
34****
35***/
36
37enum
38{
39  MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY = 100
40};
41
42static bool
43verifyTorrent (tr_torrent * tor, bool * stopFlag)
44{
45  time_t end;
46  tr_sha1_ctx_t sha;
47  tr_sys_file_t fd = TR_BAD_SYS_FILE;
48  uint64_t filePos = 0;
49  bool changed = false;
50  bool hadPiece = false;
51  time_t lastSleptAt = 0;
52  uint32_t piecePos = 0;
53  tr_file_index_t fileIndex = 0;
54  tr_file_index_t prevFileIndex = !fileIndex;
55  tr_piece_index_t pieceIndex = 0;
56  const time_t begin = tr_time ();
57  const size_t buflen = 1024 * 128; /* 128 KiB buffer */
58  uint8_t * buffer = tr_valloc (buflen);
59
60  sha = tr_sha1_init ();
61
62  tr_logAddTorDbg (tor, "%s", "verifying torrent...");
63  tr_torrentSetChecked (tor, 0);
64  while (!*stopFlag && (pieceIndex < tor->info.pieceCount))
65    {
66      uint64_t leftInPiece;
67      uint64_t bytesThisPass;
68      uint64_t leftInFile;
69      const tr_file * file = &tor->info.files[fileIndex];
70
71      /* if we're starting a new piece... */
72      if (piecePos == 0)
73        hadPiece = tr_torrentPieceIsComplete (tor, pieceIndex);
74
75      /* if we're starting a new file... */
76      if (filePos == 0 && fd == TR_BAD_SYS_FILE && fileIndex != prevFileIndex)
77        {
78          char * filename = tr_torrentFindFile (tor, fileIndex);
79          fd = filename == NULL ? TR_BAD_SYS_FILE : tr_sys_file_open (filename,
80               TR_SYS_FILE_READ | TR_SYS_FILE_SEQUENTIAL, 0, NULL);
81          tr_free (filename);
82          prevFileIndex = fileIndex;
83        }
84
85      /* figure out how much we can read this pass */
86      leftInPiece = tr_torPieceCountBytes (tor, pieceIndex) - piecePos;
87      leftInFile = file->length - filePos;
88      bytesThisPass = MIN (leftInFile, leftInPiece);
89      bytesThisPass = MIN (bytesThisPass, buflen);
90
91      /* read a bit */
92      if (fd != TR_BAD_SYS_FILE)
93        {
94          uint64_t numRead;
95          if (tr_sys_file_read_at (fd, buffer, bytesThisPass, filePos, &numRead, NULL) && numRead > 0)
96            {
97              bytesThisPass = numRead;
98              tr_sha1_update (sha, buffer, bytesThisPass);
99#if defined HAVE_POSIX_FADVISE && defined POSIX_FADV_DONTNEED
100              (void) posix_fadvise (fd, filePos, bytesThisPass, POSIX_FADV_DONTNEED);
101#endif
102            }
103        }
104
105      /* move our offsets */
106      leftInPiece -= bytesThisPass;
107      leftInFile -= bytesThisPass;
108      piecePos += bytesThisPass;
109      filePos += bytesThisPass;
110
111      /* if we're finishing a piece... */
112      if (leftInPiece == 0)
113        {
114          time_t now;
115          bool hasPiece;
116          uint8_t hash[SHA_DIGEST_LENGTH];
117
118          tr_sha1_final (sha, hash);
119          hasPiece = memcmp (hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH) == 0;
120
121          if (hasPiece || hadPiece)
122            {
123              tr_torrentSetHasPiece (tor, pieceIndex, hasPiece);
124              changed |= hasPiece != hadPiece;
125            }
126
127          tr_torrentSetPieceChecked (tor, pieceIndex);
128          now = tr_time ();
129          tor->anyDate = now;
130
131          /* sleeping even just a few msec per second goes a long
132           * way towards reducing IO load... */
133          if (lastSleptAt != now)
134            {
135              lastSleptAt = now;
136              tr_wait_msec (MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY);
137            }
138
139          sha = tr_sha1_init ();
140          pieceIndex++;
141          piecePos = 0;
142        }
143
144      /* if we're finishing a file... */
145      if (leftInFile == 0)
146        {
147          if (fd != TR_BAD_SYS_FILE)
148            {
149              tr_sys_file_close (fd, NULL);
150              fd = TR_BAD_SYS_FILE;
151            }
152          fileIndex++;
153          filePos = 0;
154        }
155    }
156
157  /* cleanup */
158  if (fd != TR_BAD_SYS_FILE)
159    tr_sys_file_close (fd, NULL);
160  tr_sha1_final (sha, NULL);
161  free (buffer);
162
163  /* stopwatch */
164  end = tr_time ();
165  tr_logAddTorDbg (tor, "Verification is done. It took %d seconds to verify %"PRIu64" bytes (%"PRIu64" bytes per second)",
166             (int)(end-begin), tor->info.totalSize,
167             (uint64_t)(tor->info.totalSize/ (1+ (end-begin))));
168
169  return changed;
170}
171
172/***
173****
174***/
175
176struct verify_node
177{
178  tr_torrent          * torrent;
179  tr_verify_done_func   callback_func;
180  void                * callback_data;
181  uint64_t              current_size;
182};
183
184static struct verify_node currentNode;
185static tr_list * verifyList = NULL;
186static tr_thread * verifyThread = NULL;
187static bool stopCurrent = false;
188
189static tr_lock*
190getVerifyLock (void)
191{
192  static tr_lock * lock = NULL;
193
194  if (lock == NULL)
195    lock = tr_lockNew ();
196
197  return lock;
198}
199
200static void
201verifyThreadFunc (void * unused UNUSED)
202{
203  for (;;)
204    {
205      int changed = 0;
206      tr_torrent * tor;
207      struct verify_node * node;
208
209      tr_lockLock (getVerifyLock ());
210      stopCurrent = false;
211      node = (struct verify_node*) verifyList ? verifyList->data : NULL;
212      if (node == NULL)
213        {
214          currentNode.torrent = NULL;
215          break;
216        }
217
218      currentNode = *node;
219      tor = currentNode.torrent;
220      tr_list_remove_data (&verifyList, node);
221      tr_free (node);
222      tr_lockUnlock (getVerifyLock ());
223
224      tr_logAddTorInfo (tor, "%s", _("Verifying torrent"));
225      tr_torrentSetVerifyState (tor, TR_VERIFY_NOW);
226      changed = verifyTorrent (tor, &stopCurrent);
227      tr_torrentSetVerifyState (tor, TR_VERIFY_NONE);
228      assert (tr_isTorrent (tor));
229
230      if (!stopCurrent && changed)
231        tr_torrentSetDirty (tor);
232
233      if (currentNode.callback_func)
234        (*currentNode.callback_func)(tor, stopCurrent, currentNode.callback_data);
235    }
236
237  verifyThread = NULL;
238  tr_lockUnlock (getVerifyLock ());
239}
240
241static int
242compareVerifyByPriorityAndSize (const void * va, const void * vb)
243{
244  const struct verify_node * a = va;
245  const struct verify_node * b = vb;
246
247  /* higher priority comes before lower priority */
248  const tr_priority_t pa = tr_torrentGetPriority (a->torrent);
249  const tr_priority_t pb = tr_torrentGetPriority (b->torrent);
250  if (pa != pb)
251    return pa > pb ? -1 : 1;
252
253  /* smaller torrents come before larger ones because they verify faster */
254  if (a->current_size < b->current_size)
255    return -1;
256  if (a->current_size > b->current_size)
257    return  1;
258  return 0;
259}
260
261void
262tr_verifyAdd (tr_torrent           * tor,
263              tr_verify_done_func    callback_func,
264              void                 * callback_data)
265{
266  struct verify_node * node;
267
268  assert (tr_isTorrent (tor));
269  tr_logAddTorInfo (tor, "%s", _("Queued for verification"));
270
271  node = tr_new (struct verify_node, 1);
272  node->torrent = tor;
273  node->callback_func = callback_func;
274  node->callback_data = callback_data;
275  node->current_size = tr_torrentGetCurrentSizeOnDisk (tor);
276
277  tr_lockLock (getVerifyLock ());
278  tr_torrentSetVerifyState (tor, TR_VERIFY_WAIT);
279  tr_list_insert_sorted (&verifyList, node, compareVerifyByPriorityAndSize);
280  if (verifyThread == NULL)
281    verifyThread = tr_threadNew (verifyThreadFunc, NULL);
282  tr_lockUnlock (getVerifyLock ());
283}
284
285static int
286compareVerifyByTorrent (const void * va, const void * vb)
287{
288  const struct verify_node * a = va;
289  const tr_torrent * b = vb;
290  return a->torrent - b;
291}
292
293void
294tr_verifyRemove (tr_torrent * tor)
295{
296  tr_lock * lock = getVerifyLock ();
297  tr_lockLock (lock);
298
299  assert (tr_isTorrent (tor));
300
301  if (tor == currentNode.torrent)
302    {
303      stopCurrent = true;
304
305      while (stopCurrent)
306        {
307          tr_lockUnlock (lock);
308          tr_wait_msec (100);
309          tr_lockLock (lock);
310        }
311    }
312  else
313    {
314      struct verify_node * node = tr_list_remove (&verifyList, tor, compareVerifyByTorrent);
315
316      tr_torrentSetVerifyState (tor, TR_VERIFY_NONE);
317
318      if (node != NULL)
319        {
320          if (node->callback_func != NULL)
321            (*node->callback_func)(tor, true, node->callback_data);
322
323          tr_free (node);
324        }
325    }
326
327  tr_lockUnlock (lock);
328}
329
330void
331tr_verifyClose (tr_session * session UNUSED)
332{
333  tr_lockLock (getVerifyLock ());
334
335  stopCurrent = true;
336  tr_list_free (&verifyList, tr_free);
337
338  tr_lockUnlock (getVerifyLock ());
339}
340
Note: See TracBrowser for help on using the repository browser.