source: trunk/libtransmission/verify.c @ 14327

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

(trunk, libt) #4160 - the slow slog to catch trunk up to mike.dld's 4160 diff continues. This step applies 4160-03b-file.patch, which replaces native file operations with the tr_sys_file_*() portability wrappers added in r14321.

  • Property svn:keywords set to Date Rev Author Id
File size: 8.4 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 14327 2014-07-28 04:13:38Z 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 "file.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  tr_sys_file_t fd = TR_BAD_SYS_FILE;
45  uint64_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      uint64_t leftInPiece;
64      uint64_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 == 0 && fd == TR_BAD_SYS_FILE && fileIndex != prevFileIndex)
74        {
75          char * filename = tr_torrentFindFile (tor, fileIndex);
76          fd = filename == NULL ? TR_BAD_SYS_FILE : tr_sys_file_open (filename,
77               TR_SYS_FILE_READ | TR_SYS_FILE_SEQUENTIAL, 0, NULL);
78          tr_free (filename);
79          prevFileIndex = fileIndex;
80        }
81
82      /* figure out how much we can read this pass */
83      leftInPiece = tr_torPieceCountBytes (tor, pieceIndex) - piecePos;
84      leftInFile = file->length - filePos;
85      bytesThisPass = MIN (leftInFile, leftInPiece);
86      bytesThisPass = MIN (bytesThisPass, buflen);
87
88      /* read a bit */
89      if (fd != TR_BAD_SYS_FILE)
90        {
91          uint64_t numRead;
92          if (tr_sys_file_read_at (fd, buffer, bytesThisPass, filePos, &numRead, NULL) && numRead > 0)
93            {
94              bytesThisPass = numRead;
95              SHA1_Update (&sha, buffer, bytesThisPass);
96#if defined HAVE_POSIX_FADVISE && defined POSIX_FADV_DONTNEED
97              posix_fadvise (fd, filePos, bytesThisPass, POSIX_FADV_DONTNEED);
98#endif
99            }
100        }
101
102      /* move our offsets */
103      leftInPiece -= bytesThisPass;
104      leftInFile -= bytesThisPass;
105      piecePos += bytesThisPass;
106      filePos += bytesThisPass;
107
108      /* if we're finishing a piece... */
109      if (leftInPiece == 0)
110        {
111          time_t now;
112          bool hasPiece;
113          uint8_t hash[SHA_DIGEST_LENGTH];
114
115          SHA1_Final (hash, &sha);
116          hasPiece = !memcmp (hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH);
117
118          if (hasPiece || hadPiece)
119            {
120              tr_torrentSetHasPiece (tor, pieceIndex, hasPiece);
121              changed |= hasPiece != hadPiece;
122            }
123
124          tr_torrentSetPieceChecked (tor, pieceIndex);
125          now = tr_time ();
126          tor->anyDate = now;
127
128          /* sleeping even just a few msec per second goes a long
129           * way towards reducing IO load... */
130          if (lastSleptAt != now)
131            {
132              lastSleptAt = now;
133              tr_wait_msec (MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY);
134            }
135
136          SHA1_Init (&sha);
137          pieceIndex++;
138          piecePos = 0;
139        }
140
141      /* if we're finishing a file... */
142      if (leftInFile == 0)
143        {
144          if (fd != TR_BAD_SYS_FILE)
145            {
146              tr_sys_file_close (fd, NULL);
147              fd = TR_BAD_SYS_FILE;
148            }
149          fileIndex++;
150          filePos = 0;
151        }
152    }
153
154  /* cleanup */
155  if (fd != TR_BAD_SYS_FILE)
156    tr_sys_file_close (fd, 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.