source: trunk/libtransmission/verify.c @ 13913

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

(libT) change the API signature for tr_torrentVerify() s.t. client code can be notified when the verify is finished

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