source: trunk/libtransmission/verify.c @ 14528

Last change on this file since 14528 was 14528, checked in by mikedld, 6 years ago

Fix some issues revealed by coverity

  • Property svn:keywords set to Date Rev Author Id
File size: 8.5 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 14528 2015-05-09 14:10:55Z mikedld $
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 "transmission.h"
19#include "completion.h"
20#include "crypto-utils.h"
21#include "file.h"
22#include "list.h"
23#include "log.h"
24#include "platform.h" /* tr_lock () */
25#include "torrent.h"
26#include "utils.h" /* tr_valloc (), tr_free () */
27#include "verify.h"
28
29/***
30****
31***/
32
33enum
34{
35  MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY = 100
36};
37
38static bool
39verifyTorrent (tr_torrent * tor, bool * stopFlag)
40{
41  time_t end;
42  tr_sha1_ctx_t sha;
43  tr_sys_file_t fd = TR_BAD_SYS_FILE;
44  uint64_t filePos = 0;
45  bool changed = false;
46  bool hadPiece = false;
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);
55
56  sha = tr_sha1_init ();
57
58  tr_logAddTorDbg (tor, "%s", "verifying torrent...");
59  tr_torrentSetChecked (tor, 0);
60  while (!*stopFlag && (pieceIndex < tor->info.pieceCount))
61    {
62      uint64_t leftInPiece;
63      uint64_t bytesThisPass;
64      uint64_t leftInFile;
65      const tr_file * file = &tor->info.files[fileIndex];
66
67      /* if we're starting a new piece... */
68      if (piecePos == 0)
69        hadPiece = tr_torrentPieceIsComplete (tor, pieceIndex);
70
71      /* if we're starting a new file... */
72      if (filePos == 0 && fd == TR_BAD_SYS_FILE && fileIndex != prevFileIndex)
73        {
74          char * filename = tr_torrentFindFile (tor, fileIndex);
75          fd = filename == NULL ? TR_BAD_SYS_FILE : tr_sys_file_open (filename,
76               TR_SYS_FILE_READ | TR_SYS_FILE_SEQUENTIAL, 0, NULL);
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 != TR_BAD_SYS_FILE)
89        {
90          uint64_t numRead;
91          if (tr_sys_file_read_at (fd, buffer, bytesThisPass, filePos, &numRead, NULL) && numRead > 0)
92            {
93              bytesThisPass = numRead;
94              tr_sha1_update (sha, buffer, bytesThisPass);
95#if defined HAVE_POSIX_FADVISE && defined POSIX_FADV_DONTNEED
96              (void) 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          tr_sha1_final (sha, hash);
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          sha = tr_sha1_init ();
136          pieceIndex++;
137          piecePos = 0;
138        }
139
140      /* if we're finishing a file... */
141      if (leftInFile == 0)
142        {
143          if (fd != TR_BAD_SYS_FILE)
144            {
145              tr_sys_file_close (fd, NULL);
146              fd = TR_BAD_SYS_FILE;
147            }
148          fileIndex++;
149          filePos = 0;
150        }
151    }
152
153  /* cleanup */
154  if (fd != TR_BAD_SYS_FILE)
155    tr_sys_file_close (fd, NULL);
156  tr_sha1_final (sha, NULL);
157  free (buffer);
158
159  /* stopwatch */
160  end = tr_time ();
161  tr_logAddTorDbg (tor, "Verification is done. It took %d seconds to verify %"PRIu64" bytes (%"PRIu64" bytes per second)",
162             (int)(end-begin), tor->info.totalSize,
163             (uint64_t)(tor->info.totalSize/ (1+ (end-begin))));
164
165  return changed;
166}
167
168/***
169****
170***/
171
172struct verify_node
173{
174  tr_torrent          * torrent;
175  tr_verify_done_func   callback_func;
176  void                * callback_data;
177  uint64_t              current_size;
178};
179
180static struct verify_node currentNode;
181static tr_list * verifyList = NULL;
182static tr_thread * verifyThread = NULL;
183static bool stopCurrent = false;
184
185static tr_lock*
186getVerifyLock (void)
187{
188  static tr_lock * lock = NULL;
189
190  if (lock == NULL)
191    lock = tr_lockNew ();
192
193  return lock;
194}
195
196static void
197verifyThreadFunc (void * unused UNUSED)
198{
199  for (;;)
200    {
201      int changed = 0;
202      tr_torrent * tor;
203      struct verify_node * node;
204
205      tr_lockLock (getVerifyLock ());
206      stopCurrent = false;
207      node = (struct verify_node*) verifyList ? verifyList->data : NULL;
208      if (node == NULL)
209        {
210          currentNode.torrent = NULL;
211          break;
212        }
213
214      currentNode = *node;
215      tor = currentNode.torrent;
216      tr_list_remove_data (&verifyList, node);
217      tr_free (node);
218      tr_lockUnlock (getVerifyLock ());
219
220      tr_logAddTorInfo (tor, "%s", _("Verifying torrent"));
221      tr_torrentSetVerifyState (tor, TR_VERIFY_NOW);
222      changed = verifyTorrent (tor, &stopCurrent);
223      tr_torrentSetVerifyState (tor, TR_VERIFY_NONE);
224      assert (tr_isTorrent (tor));
225
226      if (!stopCurrent && changed)
227        tr_torrentSetDirty (tor);
228
229      if (currentNode.callback_func)
230        (*currentNode.callback_func)(tor, stopCurrent, currentNode.callback_data);
231    }
232
233  verifyThread = NULL;
234  tr_lockUnlock (getVerifyLock ());
235}
236
237static int
238compareVerifyByPriorityAndSize (const void * va, const void * vb)
239{
240  const struct verify_node * a = va;
241  const struct verify_node * b = vb;
242
243  /* higher priority comes before lower priority */
244  const tr_priority_t pa = tr_torrentGetPriority (a->torrent);
245  const tr_priority_t pb = tr_torrentGetPriority (b->torrent);
246  if (pa != pb)
247    return pa > pb ? -1 : 1;
248
249  /* smaller torrents come before larger ones because they verify faster */
250  if (a->current_size < b->current_size)
251    return -1;
252  if (a->current_size > b->current_size)
253    return  1;
254  return 0;
255}
256
257void
258tr_verifyAdd (tr_torrent           * tor,
259              tr_verify_done_func    callback_func,
260              void                 * callback_data)
261{
262  struct verify_node * node;
263
264  assert (tr_isTorrent (tor));
265  tr_logAddTorInfo (tor, "%s", _("Queued for verification"));
266
267  node = tr_new (struct verify_node, 1);
268  node->torrent = tor;
269  node->callback_func = callback_func;
270  node->callback_data = callback_data;
271  node->current_size = tr_torrentGetCurrentSizeOnDisk (tor);
272
273  tr_lockLock (getVerifyLock ());
274  tr_torrentSetVerifyState (tor, TR_VERIFY_WAIT);
275  tr_list_insert_sorted (&verifyList, node, compareVerifyByPriorityAndSize);
276  if (verifyThread == NULL)
277    verifyThread = tr_threadNew (verifyThreadFunc, NULL);
278  tr_lockUnlock (getVerifyLock ());
279}
280
281static int
282compareVerifyByTorrent (const void * va, const void * vb)
283{
284  const struct verify_node * a = va;
285  const tr_torrent * b = vb;
286  return a->torrent - b;
287}
288
289void
290tr_verifyRemove (tr_torrent * tor)
291{
292  tr_lock * lock = getVerifyLock ();
293  tr_lockLock (lock);
294
295  assert (tr_isTorrent (tor));
296
297  if (tor == currentNode.torrent)
298    {
299      stopCurrent = true;
300
301      while (stopCurrent)
302        {
303          tr_lockUnlock (lock);
304          tr_wait_msec (100);
305          tr_lockLock (lock);
306        }
307    }
308  else
309    {
310      struct verify_node * node = tr_list_remove (&verifyList, tor, compareVerifyByTorrent);
311
312      tr_torrentSetVerifyState (tor, TR_VERIFY_NONE);
313
314      if (node != NULL)
315        {
316          if (node->callback_func != NULL)
317            (*node->callback_func)(tor, true, node->callback_data);
318
319          tr_free (node);
320        }
321    }
322
323  tr_lockUnlock (lock);
324}
325
326void
327tr_verifyClose (tr_session * session UNUSED)
328{
329  tr_lockLock (getVerifyLock ());
330
331  stopCurrent = true;
332  tr_list_free (&verifyList, tr_free);
333
334  tr_lockUnlock (getVerifyLock ());
335}
336
Note: See TracBrowser for help on using the repository browser.