source: trunk/libtransmission/inout.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.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: inout.c 14718 2016-03-13 22:11:01Z mikedld $
8 */
9
10#include <assert.h>
11#include <errno.h>
12#include <stdlib.h> /* bsearch () */
13#include <string.h> /* memcmp () */
14
15#include "transmission.h"
16#include "cache.h" /* tr_cacheReadBlock () */
17#include "crypto-utils.h"
18#include "error.h"
19#include "fdlimit.h"
20#include "file.h"
21#include "inout.h"
22#include "log.h"
23#include "peer-common.h" /* MAX_BLOCK_SIZE */
24#include "stats.h" /* tr_statsFileCreated () */
25#include "torrent.h"
26#include "utils.h"
27
28/****
29*****  Low-level IO functions
30****/
31
32enum
33{
34  TR_IO_READ,
35  TR_IO_PREFETCH,
36  /* Any operations that require write access must follow TR_IO_WRITE. */
37  TR_IO_WRITE
38};
39
40/* returns 0 on success, or an errno on failure */
41static int
42readOrWriteBytes (tr_session       * session,
43                  tr_torrent       * tor,
44                  int                ioMode,
45                  tr_file_index_t    fileIndex,
46                  uint64_t           fileOffset,
47                  void             * buf,
48                  size_t             buflen)
49{
50  tr_sys_file_t fd;
51  int err = 0;
52  const bool doWrite = ioMode >= TR_IO_WRITE;
53  const tr_info * const info = &tor->info;
54  const tr_file * const file = &info->files[fileIndex];
55
56  assert (fileIndex < info->fileCount);
57  assert (!file->length || (fileOffset < file->length));
58  assert (fileOffset + buflen <= file->length);
59
60  if (!file->length)
61    return 0;
62
63  /***
64  ****  Find the fd
65  ***/
66
67  fd = tr_fdFileGetCached (session, tr_torrentId (tor), fileIndex, doWrite);
68  if (fd == TR_BAD_SYS_FILE)
69    {
70      /* it's not cached, so open/create it now */
71      char * subpath;
72      const char * base;
73
74      /* see if the file exists... */
75      if (!tr_torrentFindFile2 (tor, fileIndex, &base, &subpath, NULL))
76        {
77          /* we can't read a file that doesn't exist... */
78          if (!doWrite)
79            err = ENOENT;
80
81          /* figure out where the file should go, so we can create it */
82          base = tr_torrentGetCurrentDir (tor);
83          subpath = tr_sessionIsIncompleteFileNamingEnabled (tor->session)
84                  ? tr_torrentBuildPartial (tor, fileIndex)
85                  : tr_strdup (file->name);
86
87        }
88
89      if (!err)
90        {
91          /* open (and maybe create) the file */
92          char * filename = tr_buildPath (base, subpath, NULL);
93          const int prealloc = file->dnd || !doWrite
94                             ? TR_PREALLOCATE_NONE
95                             : tor->session->preallocationMode;
96          if (((fd = tr_fdFileCheckout (session, tor->uniqueId, fileIndex,
97                                        filename, doWrite,
98                                        prealloc, file->length))) == TR_BAD_SYS_FILE)
99            {
100              err = errno;
101              tr_logAddTorErr (tor, "tr_fdFileCheckout failed for \"%s\": %s",
102                         filename, tr_strerror (err));
103            }
104          else if (doWrite)
105            {
106              /* make a note that we just created a file */
107              tr_statsFileCreated (tor->session);
108            }
109
110          tr_free (filename);
111        }
112
113      tr_free (subpath);
114    }
115
116  /***
117  ****  Use the fd
118  ***/
119
120  if (!err)
121    {
122      tr_error * error = NULL;
123
124      if (ioMode == TR_IO_READ)
125        {
126          if (!tr_sys_file_read_at (fd, buf, buflen, fileOffset, NULL, &error))
127            {
128              err = error->code;
129              tr_logAddTorErr (tor, "read failed for \"%s\": %s", file->name, error->message);
130              tr_error_free (error);
131            }
132        }
133      else if (ioMode == TR_IO_WRITE)
134        {
135          if (!tr_sys_file_write_at (fd, buf, buflen, fileOffset, NULL, &error))
136            {
137              err = error->code;
138              tr_logAddTorErr (tor, "write failed for \"%s\": %s", file->name, error->message);
139              tr_error_free (error);
140            }
141        }
142      else if (ioMode == TR_IO_PREFETCH)
143        {
144          tr_sys_file_prefetch (fd, fileOffset, buflen, NULL);
145        }
146      else
147        {
148          abort ();
149        }
150    }
151
152  return err;
153}
154
155static int
156compareOffsetToFile (const void * a, const void * b)
157{
158  const uint64_t  offset = * (const uint64_t*)a;
159  const tr_file * file = b;
160
161  if (offset < file->offset)
162    return -1;
163
164  if (offset >= file->offset + file->length)
165    return 1;
166
167  return 0;
168}
169
170void
171tr_ioFindFileLocation (const tr_torrent * tor,
172                       tr_piece_index_t   pieceIndex,
173                       uint32_t           pieceOffset,
174                       tr_file_index_t  * fileIndex,
175                       uint64_t         * fileOffset)
176{
177  const uint64_t  offset = tr_pieceOffset (tor, pieceIndex, pieceOffset, 0);
178  const tr_file * file;
179
180  assert (tr_isTorrent (tor));
181  assert (offset < tor->info.totalSize);
182
183  file = bsearch (&offset,
184                  tor->info.files, tor->info.fileCount, sizeof (tr_file),
185                  compareOffsetToFile);
186
187  assert (file != NULL);
188
189  *fileIndex = file - tor->info.files;
190  *fileOffset = offset - file->offset;
191
192  assert (*fileIndex < tor->info.fileCount);
193  assert (*fileOffset < file->length);
194  assert (tor->info.files[*fileIndex].offset + *fileOffset == offset);
195}
196
197/* returns 0 on success, or an errno on failure */
198static int
199readOrWritePiece (tr_torrent       * tor,
200                  int                ioMode,
201                  tr_piece_index_t   pieceIndex,
202                  uint32_t           pieceOffset,
203                  uint8_t          * buf,
204                  size_t             buflen)
205{
206  int err = 0;
207  tr_file_index_t fileIndex;
208  uint64_t fileOffset;
209  const tr_info * info = &tor->info;
210
211  if (pieceIndex >= tor->info.pieceCount)
212    return EINVAL;
213
214  tr_ioFindFileLocation (tor, pieceIndex, pieceOffset,
215                         &fileIndex, &fileOffset);
216
217  while (buflen && !err)
218    {
219      const tr_file * file = &info->files[fileIndex];
220      const uint64_t bytesThisPass = MIN (buflen, file->length - fileOffset);
221
222      err = readOrWriteBytes (tor->session, tor, ioMode, fileIndex, fileOffset, buf, bytesThisPass);
223      buf += bytesThisPass;
224      buflen -= bytesThisPass;
225      fileIndex++;
226      fileOffset = 0;
227
228      if ((err != 0) && (ioMode == TR_IO_WRITE) && (tor->error != TR_STAT_LOCAL_ERROR))
229        {
230          char * path = tr_buildPath (tor->downloadDir, file->name, NULL);
231          tr_torrentSetLocalError (tor, "%s (%s)", tr_strerror (err), path);
232          tr_free (path);
233        }
234    }
235
236  return err;
237}
238
239int
240tr_ioRead (tr_torrent       * tor,
241           tr_piece_index_t   pieceIndex,
242           uint32_t           begin,
243           uint32_t           len,
244           uint8_t          * buf)
245{
246  return readOrWritePiece (tor, TR_IO_READ, pieceIndex, begin, buf, len);
247}
248
249int
250tr_ioPrefetch (tr_torrent       * tor,
251               tr_piece_index_t   pieceIndex,
252               uint32_t           begin,
253               uint32_t           len)
254{
255  return readOrWritePiece (tor, TR_IO_PREFETCH, pieceIndex, begin, NULL, len);
256}
257
258int
259tr_ioWrite (tr_torrent       * tor,
260            tr_piece_index_t   pieceIndex,
261            uint32_t           begin,
262            uint32_t           len,
263            const uint8_t    * buf)
264{
265  return readOrWritePiece (tor, TR_IO_WRITE, pieceIndex, begin, (uint8_t*)buf, len);
266}
267
268/****
269*****
270****/
271
272static bool
273recalculateHash (tr_torrent * tor, tr_piece_index_t pieceIndex, uint8_t * setme)
274{
275  size_t   bytesLeft;
276  uint32_t offset = 0;
277  bool  success = true;
278  const size_t buflen = tor->blockSize;
279  void * buffer = tr_valloc (buflen);
280  tr_sha1_ctx_t sha;
281
282  assert (tor != NULL);
283  assert (pieceIndex < tor->info.pieceCount);
284  assert (buffer != NULL);
285  assert (buflen > 0);
286  assert (setme != NULL);
287
288  sha = tr_sha1_init ();
289  bytesLeft = tr_torPieceCountBytes (tor, pieceIndex);
290
291  tr_ioPrefetch (tor, pieceIndex, offset, bytesLeft);
292
293  while (bytesLeft)
294    {
295      const size_t len = MIN (bytesLeft, buflen);
296      success = !tr_cacheReadBlock (tor->session->cache, tor, pieceIndex, offset, len, buffer);
297      if (!success)
298        break;
299      tr_sha1_update (sha, buffer, len);
300      offset += len;
301      bytesLeft -= len;
302    }
303
304  tr_sha1_final (sha, success ? setme : NULL);
305
306  tr_free (buffer);
307  return success;
308}
309
310bool
311tr_ioTestPiece (tr_torrent * tor, tr_piece_index_t piece)
312{
313  uint8_t hash[SHA_DIGEST_LENGTH];
314
315  return recalculateHash (tor, piece, hash)
316      && memcmp (hash, tor->info.pieces[piece].hash, SHA_DIGEST_LENGTH) == 0;
317}
Note: See TracBrowser for help on using the repository browser.