source: trunk/libtransmission/inout.c @ 14241

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

Copyedit the license's revised text: (1) remove unnecessary repitition use of the word 'license' from the top of the header and source files (2) add the standard 'we hope it's useful, but no warranty' clause to COPYING (3) make explicit that linking OpenSSL is allowed (see https://people.gnome.org/~markmc/openssl-and-the-gpl.html for background) (4) sync the Qt and GTK+ clients' license popups with COPYING's revised text

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