2007-06-18 03:40:41 +00:00
|
|
|
/*
|
2014-01-19 01:09:44 +00:00
|
|
|
* This file Copyright (C) 2007-2014 Mnemosyne LLC
|
2006-07-16 19:39:23 +00:00
|
|
|
*
|
2014-01-21 03:10:30 +00:00
|
|
|
* It may be used under the GNU GPL versions 2 or 3
|
2014-01-19 01:09:44 +00:00
|
|
|
* or any future license endorsed by Mnemosyne LLC.
|
2007-08-18 17:19:49 +00:00
|
|
|
*
|
2007-06-18 03:40:41 +00:00
|
|
|
*/
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2007-11-09 20:07:52 +00:00
|
|
|
#include <assert.h>
|
2008-02-19 04:16:04 +00:00
|
|
|
#include <errno.h>
|
2012-12-05 17:29:46 +00:00
|
|
|
#include <stdlib.h> /* bsearch () */
|
|
|
|
#include <string.h> /* memcmp () */
|
2007-07-29 18:11:21 +00:00
|
|
|
|
2006-07-16 19:39:23 +00:00
|
|
|
#include "transmission.h"
|
2012-12-05 17:29:46 +00:00
|
|
|
#include "cache.h" /* tr_cacheReadBlock () */
|
2014-12-04 12:13:59 +00:00
|
|
|
#include "crypto-utils.h"
|
2014-07-28 04:13:38 +00:00
|
|
|
#include "error.h"
|
2007-07-09 20:10:42 +00:00
|
|
|
#include "fdlimit.h"
|
2014-07-28 04:13:38 +00:00
|
|
|
#include "file.h"
|
2007-07-30 18:04:10 +00:00
|
|
|
#include "inout.h"
|
2013-01-25 23:34:20 +00:00
|
|
|
#include "log.h"
|
2010-07-08 23:04:12 +00:00
|
|
|
#include "peer-common.h" /* MAX_BLOCK_SIZE */
|
2012-12-05 17:29:46 +00:00
|
|
|
#include "stats.h" /* tr_statsFileCreated () */
|
2007-12-25 05:37:32 +00:00
|
|
|
#include "torrent.h"
|
2007-07-30 18:04:10 +00:00
|
|
|
#include "utils.h"
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2007-06-18 03:40:41 +00:00
|
|
|
/****
|
|
|
|
***** Low-level IO functions
|
|
|
|
****/
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
enum
|
|
|
|
{
|
|
|
|
TR_IO_READ,
|
|
|
|
TR_IO_PREFETCH,
|
|
|
|
/* Any operations that require write access must follow TR_IO_WRITE. */
|
|
|
|
TR_IO_WRITE
|
2009-11-08 23:43:38 +00:00
|
|
|
};
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2008-10-03 04:49:06 +00:00
|
|
|
/* returns 0 on success, or an errno on failure */
|
|
|
|
static int
|
2012-12-05 17:29:46 +00:00
|
|
|
readOrWriteBytes (tr_session * session,
|
2010-12-27 21:34:12 +00:00
|
|
|
tr_torrent * tor,
|
2008-09-23 19:11:04 +00:00
|
|
|
int ioMode,
|
|
|
|
tr_file_index_t fileIndex,
|
|
|
|
uint64_t fileOffset,
|
2010-12-27 21:34:12 +00:00
|
|
|
void * buf,
|
2012-12-05 17:29:46 +00:00
|
|
|
size_t buflen)
|
2007-06-18 03:40:41 +00:00
|
|
|
{
|
2014-07-28 04:13:38 +00:00
|
|
|
tr_sys_file_t fd;
|
2012-12-05 17:29:46 +00:00
|
|
|
int err = 0;
|
|
|
|
const bool doWrite = ioMode >= TR_IO_WRITE;
|
|
|
|
const tr_info * const info = &tor->info;
|
|
|
|
const tr_file * const file = &info->files[fileIndex];
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
assert (fileIndex < info->fileCount);
|
|
|
|
assert (!file->length || (fileOffset < file->length));
|
|
|
|
assert (fileOffset + buflen <= file->length);
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
if (!file->length)
|
|
|
|
return 0;
|
2008-01-18 19:13:32 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
/***
|
|
|
|
**** Find the fd
|
|
|
|
***/
|
2009-01-16 16:38:16 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
fd = tr_fdFileGetCached (session, tr_torrentId (tor), fileIndex, doWrite);
|
2014-07-28 04:13:38 +00:00
|
|
|
if (fd == TR_BAD_SYS_FILE)
|
2009-10-19 05:05:00 +00:00
|
|
|
{
|
2012-12-05 17:29:46 +00:00
|
|
|
/* it's not cached, so open/create it now */
|
|
|
|
char * subpath;
|
|
|
|
const char * base;
|
2009-10-19 05:05:00 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
/* see if the file exists... */
|
|
|
|
if (!tr_torrentFindFile2 (tor, fileIndex, &base, &subpath, NULL))
|
2009-10-20 03:14:44 +00:00
|
|
|
{
|
2012-12-05 17:29:46 +00:00
|
|
|
/* we can't read a file that doesn't exist... */
|
|
|
|
if (!doWrite)
|
|
|
|
err = ENOENT;
|
2011-03-16 04:44:38 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
/* figure out where the file should go, so we can create it */
|
|
|
|
base = tr_torrentGetCurrentDir (tor);
|
|
|
|
subpath = tr_sessionIsIncompleteFileNamingEnabled (tor->session)
|
|
|
|
? tr_torrentBuildPartial (tor, fileIndex)
|
|
|
|
: tr_strdup (file->name);
|
2009-10-19 05:05:00 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
if (!err)
|
2009-10-19 05:05:00 +00:00
|
|
|
{
|
2012-12-05 17:29:46 +00:00
|
|
|
/* open (and maybe create) the file */
|
|
|
|
char * filename = tr_buildPath (base, subpath, NULL);
|
|
|
|
const int prealloc = file->dnd || !doWrite
|
|
|
|
? TR_PREALLOCATE_NONE
|
|
|
|
: tor->session->preallocationMode;
|
|
|
|
if (((fd = tr_fdFileCheckout (session, tor->uniqueId, fileIndex,
|
|
|
|
filename, doWrite,
|
2014-07-28 04:13:38 +00:00
|
|
|
prealloc, file->length))) == TR_BAD_SYS_FILE)
|
2009-10-21 19:33:37 +00:00
|
|
|
{
|
2012-12-05 17:29:46 +00:00
|
|
|
err = errno;
|
2013-01-25 23:34:20 +00:00
|
|
|
tr_logAddTorErr (tor, "tr_fdFileCheckout failed for \"%s\": %s",
|
2012-12-05 17:29:46 +00:00
|
|
|
filename, tr_strerror (err));
|
2011-03-16 04:44:38 +00:00
|
|
|
}
|
2012-12-05 17:29:46 +00:00
|
|
|
else if (doWrite)
|
2011-03-16 04:44:38 +00:00
|
|
|
{
|
2012-12-05 17:29:46 +00:00
|
|
|
/* make a note that we just created a file */
|
|
|
|
tr_statsFileCreated (tor->session);
|
2009-10-21 19:33:37 +00:00
|
|
|
}
|
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
tr_free (filename);
|
2009-10-19 05:05:00 +00:00
|
|
|
}
|
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
tr_free (subpath);
|
2009-09-08 06:25:40 +00:00
|
|
|
}
|
2007-11-26 20:37:07 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
/***
|
|
|
|
**** Use the fd
|
|
|
|
***/
|
2011-03-16 04:44:38 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
if (!err)
|
2011-02-02 06:06:09 +00:00
|
|
|
{
|
2014-07-28 04:13:38 +00:00
|
|
|
tr_error * error = NULL;
|
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
if (ioMode == TR_IO_READ)
|
|
|
|
{
|
2014-07-28 04:13:38 +00:00
|
|
|
if (!tr_sys_file_read_at (fd, buf, buflen, fileOffset, NULL, &error))
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2014-07-28 04:13:38 +00:00
|
|
|
err = error->code;
|
|
|
|
tr_logAddTorErr (tor, "read failed for \"%s\": %s", file->name, error->message);
|
|
|
|
tr_error_free (error);
|
2009-11-08 23:43:38 +00:00
|
|
|
}
|
2012-12-05 17:29:46 +00:00
|
|
|
}
|
|
|
|
else if (ioMode == TR_IO_WRITE)
|
|
|
|
{
|
2014-07-28 04:13:38 +00:00
|
|
|
if (!tr_sys_file_write_at (fd, buf, buflen, fileOffset, NULL, &error))
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2014-07-28 04:13:38 +00:00
|
|
|
err = error->code;
|
|
|
|
tr_logAddTorErr (tor, "write failed for \"%s\": %s", file->name, error->message);
|
|
|
|
tr_error_free (error);
|
2009-11-08 23:43:38 +00:00
|
|
|
}
|
2012-12-05 17:29:46 +00:00
|
|
|
}
|
|
|
|
else if (ioMode == TR_IO_PREFETCH)
|
|
|
|
{
|
2014-07-28 04:13:38 +00:00
|
|
|
tr_sys_file_prefetch (fd, fileOffset, buflen, NULL);
|
2012-12-05 17:29:46 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
abort ();
|
2009-11-25 19:26:30 +00:00
|
|
|
}
|
2009-10-19 05:05:00 +00:00
|
|
|
}
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
return err;
|
2007-05-02 19:35:34 +00:00
|
|
|
}
|
|
|
|
|
2008-04-20 21:54:44 +00:00
|
|
|
static int
|
2012-12-05 17:29:46 +00:00
|
|
|
compareOffsetToFile (const void * a, const void * b)
|
2008-04-20 21:54:44 +00:00
|
|
|
{
|
2012-12-05 17:29:46 +00:00
|
|
|
const uint64_t offset = * (const uint64_t*)a;
|
|
|
|
const tr_file * file = b;
|
2008-04-20 21:54:44 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
if (offset < file->offset)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (offset >= file->offset + file->length)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
2008-04-20 21:54:44 +00:00
|
|
|
}
|
|
|
|
|
2008-06-07 21:26:41 +00:00
|
|
|
void
|
2012-12-05 17:29:46 +00:00
|
|
|
tr_ioFindFileLocation (const tr_torrent * tor,
|
2008-06-07 21:26:41 +00:00
|
|
|
tr_piece_index_t pieceIndex,
|
|
|
|
uint32_t pieceOffset,
|
2008-11-20 17:19:59 +00:00
|
|
|
tr_file_index_t * fileIndex,
|
2012-12-05 17:29:46 +00:00
|
|
|
uint64_t * fileOffset)
|
2006-07-16 19:39:23 +00:00
|
|
|
{
|
2012-12-05 17:29:46 +00:00
|
|
|
const uint64_t offset = tr_pieceOffset (tor, pieceIndex, pieceOffset, 0);
|
|
|
|
const tr_file * file;
|
2008-04-20 21:54:44 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
assert (tr_isTorrent (tor));
|
|
|
|
assert (offset < tor->info.totalSize);
|
2010-10-14 17:12:12 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
file = bsearch (&offset,
|
|
|
|
tor->info.files, tor->info.fileCount, sizeof (tr_file),
|
|
|
|
compareOffsetToFile);
|
2010-10-14 17:12:12 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
assert (file != NULL);
|
2010-10-14 04:21:14 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
*fileIndex = file - tor->info.files;
|
|
|
|
*fileOffset = offset - file->offset;
|
2008-04-20 21:54:44 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
assert (*fileIndex < tor->info.fileCount);
|
|
|
|
assert (*fileOffset < file->length);
|
|
|
|
assert (tor->info.files[*fileIndex].offset + *fileOffset == offset);
|
2006-07-16 19:39:23 +00:00
|
|
|
}
|
|
|
|
|
2008-10-03 04:49:06 +00:00
|
|
|
/* returns 0 on success, or an errno on failure */
|
|
|
|
static int
|
2012-12-05 17:29:46 +00:00
|
|
|
readOrWritePiece (tr_torrent * tor,
|
2008-09-23 19:11:04 +00:00
|
|
|
int ioMode,
|
|
|
|
tr_piece_index_t pieceIndex,
|
|
|
|
uint32_t pieceOffset,
|
2009-08-13 17:25:26 +00:00
|
|
|
uint8_t * buf,
|
2012-12-05 17:29:46 +00:00
|
|
|
size_t buflen)
|
2007-06-18 03:40:41 +00:00
|
|
|
{
|
2012-12-05 17:29:46 +00:00
|
|
|
int err = 0;
|
|
|
|
tr_file_index_t fileIndex;
|
|
|
|
uint64_t fileOffset;
|
|
|
|
const tr_info * info = &tor->info;
|
2007-05-27 23:32:26 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
if (pieceIndex >= tor->info.pieceCount)
|
|
|
|
return EINVAL;
|
2007-05-27 23:32:26 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
tr_ioFindFileLocation (tor, pieceIndex, pieceOffset,
|
|
|
|
&fileIndex, &fileOffset);
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
while (buflen && !err)
|
2006-07-16 19:39:23 +00:00
|
|
|
{
|
2012-12-05 17:29:46 +00:00
|
|
|
const tr_file * file = &info->files[fileIndex];
|
|
|
|
const uint64_t bytesThisPass = MIN (buflen, file->length - fileOffset);
|
2007-06-18 03:40:41 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
err = readOrWriteBytes (tor->session, tor, ioMode, fileIndex, fileOffset, buf, bytesThisPass);
|
|
|
|
buf += bytesThisPass;
|
|
|
|
buflen -= bytesThisPass;
|
|
|
|
fileIndex++;
|
|
|
|
fileOffset = 0;
|
2009-08-13 17:25:26 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
if ((err != 0) && (ioMode == TR_IO_WRITE) && (tor->error != TR_STAT_LOCAL_ERROR))
|
2010-01-26 21:41:40 +00:00
|
|
|
{
|
2012-12-05 17:29:46 +00:00
|
|
|
char * path = tr_buildPath (tor->downloadDir, file->name, NULL);
|
|
|
|
tr_torrentSetLocalError (tor, "%s (%s)", tr_strerror (err), path);
|
|
|
|
tr_free (path);
|
2009-08-13 17:25:26 +00:00
|
|
|
}
|
2006-07-16 19:39:23 +00:00
|
|
|
}
|
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
return err;
|
2006-07-16 19:39:23 +00:00
|
|
|
}
|
|
|
|
|
2008-10-03 04:49:06 +00:00
|
|
|
int
|
2012-12-05 17:29:46 +00:00
|
|
|
tr_ioRead (tr_torrent * tor,
|
2008-09-23 19:11:04 +00:00
|
|
|
tr_piece_index_t pieceIndex,
|
|
|
|
uint32_t begin,
|
|
|
|
uint32_t len,
|
2012-12-05 17:29:46 +00:00
|
|
|
uint8_t * buf)
|
2006-07-16 19:39:23 +00:00
|
|
|
{
|
2012-12-05 17:29:46 +00:00
|
|
|
return readOrWritePiece (tor, TR_IO_READ, pieceIndex, begin, buf, len);
|
2006-07-16 19:39:23 +00:00
|
|
|
}
|
|
|
|
|
2009-11-08 23:43:38 +00:00
|
|
|
int
|
2012-12-05 17:29:46 +00:00
|
|
|
tr_ioPrefetch (tr_torrent * tor,
|
2009-11-08 23:43:38 +00:00
|
|
|
tr_piece_index_t pieceIndex,
|
|
|
|
uint32_t begin,
|
|
|
|
uint32_t len)
|
|
|
|
{
|
2012-12-05 17:29:46 +00:00
|
|
|
return readOrWritePiece (tor, TR_IO_PREFETCH, pieceIndex, begin, NULL, len);
|
2009-11-08 23:43:38 +00:00
|
|
|
}
|
|
|
|
|
2008-10-03 04:49:06 +00:00
|
|
|
int
|
2012-12-05 17:29:46 +00:00
|
|
|
tr_ioWrite (tr_torrent * tor,
|
2008-09-23 19:11:04 +00:00
|
|
|
tr_piece_index_t pieceIndex,
|
|
|
|
uint32_t begin,
|
|
|
|
uint32_t len,
|
2012-12-05 17:29:46 +00:00
|
|
|
const uint8_t * buf)
|
2006-07-16 19:39:23 +00:00
|
|
|
{
|
2012-12-05 17:29:46 +00:00
|
|
|
return readOrWritePiece (tor, TR_IO_WRITE, pieceIndex, begin, (uint8_t*)buf, len);
|
2006-07-16 19:39:23 +00:00
|
|
|
}
|
|
|
|
|
2007-06-18 03:40:41 +00:00
|
|
|
/****
|
|
|
|
*****
|
|
|
|
****/
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2011-03-22 15:19:54 +00:00
|
|
|
static bool
|
2012-12-05 17:29:46 +00:00
|
|
|
recalculateHash (tr_torrent * tor, tr_piece_index_t pieceIndex, uint8_t * setme)
|
2007-06-18 03:40:41 +00:00
|
|
|
{
|
2012-12-05 17:29:46 +00:00
|
|
|
size_t bytesLeft;
|
|
|
|
uint32_t offset = 0;
|
|
|
|
bool success = true;
|
|
|
|
const size_t buflen = tor->blockSize;
|
|
|
|
void * buffer = tr_valloc (buflen);
|
2014-12-04 12:13:59 +00:00
|
|
|
tr_sha1_ctx_t sha;
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
assert (tor != NULL);
|
|
|
|
assert (pieceIndex < tor->info.pieceCount);
|
|
|
|
assert (buffer != NULL);
|
|
|
|
assert (buflen > 0);
|
|
|
|
assert (setme != NULL);
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2014-12-04 12:13:59 +00:00
|
|
|
sha = tr_sha1_init ();
|
2012-12-05 17:29:46 +00:00
|
|
|
bytesLeft = tr_torPieceCountBytes (tor, pieceIndex);
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
tr_ioPrefetch (tor, pieceIndex, offset, bytesLeft);
|
2010-04-30 00:04:15 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
while (bytesLeft)
|
2008-09-06 13:25:21 +00:00
|
|
|
{
|
2015-03-15 11:43:32 +00:00
|
|
|
const size_t len = MIN (bytesLeft, buflen);
|
2012-12-05 17:29:46 +00:00
|
|
|
success = !tr_cacheReadBlock (tor->session->cache, tor, pieceIndex, offset, len, buffer);
|
|
|
|
if (!success)
|
|
|
|
break;
|
2014-12-04 12:13:59 +00:00
|
|
|
tr_sha1_update (sha, buffer, len);
|
2012-12-05 17:29:46 +00:00
|
|
|
offset += len;
|
|
|
|
bytesLeft -= len;
|
2008-01-27 17:03:58 +00:00
|
|
|
}
|
2008-09-06 13:25:21 +00:00
|
|
|
|
2014-12-04 12:13:59 +00:00
|
|
|
tr_sha1_final (sha, success ? setme : NULL);
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
tr_free (buffer);
|
|
|
|
return success;
|
2006-07-16 19:39:23 +00:00
|
|
|
}
|
|
|
|
|
2011-03-22 15:19:54 +00:00
|
|
|
bool
|
2012-12-05 17:29:46 +00:00
|
|
|
tr_ioTestPiece (tr_torrent * tor, tr_piece_index_t piece)
|
2006-07-16 19:39:23 +00:00
|
|
|
{
|
2012-12-05 17:29:46 +00:00
|
|
|
uint8_t hash[SHA_DIGEST_LENGTH];
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2012-12-05 17:29:46 +00:00
|
|
|
return recalculateHash (tor, piece, hash)
|
2016-03-13 22:11:01 +00:00
|
|
|
&& memcmp (hash, tor->info.pieces[piece].hash, SHA_DIGEST_LENGTH) == 0;
|
2008-12-31 18:08:13 +00:00
|
|
|
}
|