source: trunk/libtransmission/inout.c @ 13625

Last change on this file since 13625 was 13625, checked in by jordan, 9 years ago

Follow more common whitespace style conventions in the C code (libtransmission, daemon, utils, cli, gtk).

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