2022-01-20 18:27:56 +00:00
|
|
|
// This file Copyright © 2007-2022 Mnemosyne LLC.
|
2022-02-07 16:25:02 +00:00
|
|
|
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
2022-01-20 18:27:56 +00:00
|
|
|
// or any future license endorsed by Mnemosyne LLC.
|
|
|
|
// License text can be found in the licenses/ folder.
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2021-09-19 20:41:35 +00:00
|
|
|
#include <algorithm>
|
|
|
|
#include <cerrno>
|
2021-10-29 18:24:30 +00:00
|
|
|
#include <optional>
|
2021-11-09 03:30:03 +00:00
|
|
|
#include <vector>
|
2007-07-29 18:11:21 +00:00
|
|
|
|
2022-03-14 04:43:35 +00:00
|
|
|
#include <fmt/core.h>
|
|
|
|
|
2006-07-16 19:39:23 +00:00
|
|
|
#include "transmission.h"
|
2022-02-22 17:21:24 +00:00
|
|
|
|
2017-04-21 07:40:57 +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"
|
|
|
|
#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"
|
2007-12-25 05:37:32 +00:00
|
|
|
#include "torrent.h"
|
2017-06-08 07:24:12 +00:00
|
|
|
#include "tr-assert.h"
|
2007-07-30 18:04:10 +00:00
|
|
|
#include "utils.h"
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2021-12-23 17:16:05 +00:00
|
|
|
using namespace std::literals;
|
|
|
|
|
2022-02-22 17:21:24 +00:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
|
|
|
bool readEntireBuf(tr_sys_file_t fd, uint64_t file_offset, uint8_t* buf, uint64_t buflen, tr_error** error)
|
|
|
|
{
|
|
|
|
while (buflen > 0)
|
|
|
|
{
|
|
|
|
auto n_read = uint64_t{};
|
|
|
|
|
|
|
|
if (!tr_sys_file_read_at(fd, buf, buflen, file_offset, &n_read, error))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf += n_read;
|
|
|
|
buflen -= n_read;
|
|
|
|
file_offset += n_read;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool writeEntireBuf(tr_sys_file_t fd, uint64_t file_offset, uint8_t const* buf, uint64_t buflen, tr_error** error)
|
|
|
|
{
|
|
|
|
while (buflen > 0)
|
|
|
|
{
|
|
|
|
auto n_written = uint64_t{};
|
|
|
|
|
|
|
|
if (!tr_sys_file_write_at(fd, buf, buflen, file_offset, &n_written, error))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf += n_written;
|
|
|
|
buflen -= n_written;
|
|
|
|
file_offset += n_written;
|
|
|
|
}
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2022-02-22 17:21:24 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum class IoMode
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2022-02-22 17:21:24 +00:00
|
|
|
Read,
|
|
|
|
Prefetch,
|
|
|
|
Write
|
2009-11-08 23:43:38 +00:00
|
|
|
};
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2022-06-01 16:56:59 +00:00
|
|
|
bool getFilename(tr_pathbuf& setme, tr_torrent const* tor, tr_file_index_t file_index, IoMode io_mode)
|
|
|
|
{
|
|
|
|
if (auto found = tor->findFile(file_index); found)
|
|
|
|
{
|
|
|
|
setme.assign(found->filename());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (io_mode != IoMode::Write)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We didn't find the file that we want to write to.
|
|
|
|
// Let's figure out where it goes so that we can create it.
|
|
|
|
auto const base = tor->currentDir();
|
2022-08-15 00:33:28 +00:00
|
|
|
auto const suffix = tor->session->isIncompleteFileNamingEnabled() ? tr_torrent_files::PartialFileSuffix : ""sv;
|
2022-06-01 16:56:59 +00:00
|
|
|
setme.assign(base, '/', tor->fileSubpath(file_index), suffix);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-10-03 04:49:06 +00:00
|
|
|
/* returns 0 on success, or an errno on failure */
|
2022-02-22 17:21:24 +00:00
|
|
|
int readOrWriteBytes(
|
2021-08-15 09:41:48 +00:00
|
|
|
tr_session* session,
|
|
|
|
tr_torrent* tor,
|
2022-02-22 17:21:24 +00:00
|
|
|
IoMode io_mode,
|
2022-01-02 01:25:25 +00:00
|
|
|
tr_file_index_t file_index,
|
|
|
|
uint64_t file_offset,
|
2022-02-22 17:21:24 +00:00
|
|
|
uint8_t* buf,
|
2021-08-15 09:41:48 +00:00
|
|
|
size_t buflen)
|
2007-06-18 03:40:41 +00:00
|
|
|
{
|
2022-01-16 18:02:47 +00:00
|
|
|
TR_ASSERT(file_index < tor->fileCount());
|
|
|
|
|
2022-03-30 18:25:41 +00:00
|
|
|
bool const do_write = io_mode == IoMode::Write;
|
2022-01-02 01:25:25 +00:00
|
|
|
auto const file_size = tor->fileSize(file_index);
|
|
|
|
TR_ASSERT(file_size == 0 || file_offset < file_size);
|
|
|
|
TR_ASSERT(file_offset + buflen <= file_size);
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2022-01-02 01:25:25 +00:00
|
|
|
if (file_size == 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2008-01-18 19:13:32 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/***
|
|
|
|
**** Find the fd
|
|
|
|
***/
|
2009-01-16 16:38:16 +00:00
|
|
|
|
2022-06-17 15:43:04 +00:00
|
|
|
auto fd = session->openFiles().get(tor->id(), file_index, do_write);
|
2022-06-11 19:35:08 +00:00
|
|
|
auto filename = tr_pathbuf{};
|
|
|
|
if (!fd && !getFilename(filename, tor, file_index, io_mode))
|
2009-10-19 05:05:00 +00:00
|
|
|
{
|
2022-06-11 19:35:08 +00:00
|
|
|
return ENOENT;
|
|
|
|
}
|
2009-10-19 05:05:00 +00:00
|
|
|
|
2022-06-11 19:35:08 +00:00
|
|
|
if (!fd) // not in the cache, so open or create it now
|
|
|
|
{
|
2022-06-01 16:56:59 +00:00
|
|
|
// open (and maybe create) the file
|
|
|
|
auto const prealloc = (!do_write || !tor->fileIsWanted(file_index)) ? TR_PREALLOCATE_NONE :
|
2022-08-14 01:04:36 +00:00
|
|
|
tor->session->preallocationMode();
|
2022-06-17 15:43:04 +00:00
|
|
|
fd = session->openFiles().get(tor->id(), file_index, do_write, filename, prealloc, file_size);
|
2022-06-11 19:35:08 +00:00
|
|
|
if (fd && do_write)
|
2022-06-01 16:56:59 +00:00
|
|
|
{
|
|
|
|
// make a note that we just created a file
|
2022-07-28 23:56:40 +00:00
|
|
|
tor->session->addFileCreated();
|
2022-06-01 16:56:59 +00:00
|
|
|
}
|
2022-02-22 17:21:24 +00:00
|
|
|
}
|
|
|
|
|
2022-06-11 19:35:08 +00:00
|
|
|
if (!fd) // couldn't create/open it either
|
|
|
|
{
|
|
|
|
int const err = errno;
|
|
|
|
tr_logAddErrorTor(
|
|
|
|
tor,
|
|
|
|
fmt::format(
|
|
|
|
_("Couldn't get '{path}': {error} ({error_code})"),
|
|
|
|
fmt::arg("path", filename),
|
|
|
|
fmt::arg("error", tr_strerror(err)),
|
|
|
|
fmt::arg("error_code", err)));
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2022-02-22 17:21:24 +00:00
|
|
|
switch (io_mode)
|
|
|
|
{
|
|
|
|
case IoMode::Read:
|
2022-06-01 16:56:59 +00:00
|
|
|
if (tr_error* error = nullptr; !readEntireBuf(*fd, file_offset, buf, buflen, &error) && error != nullptr)
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2022-06-01 16:56:59 +00:00
|
|
|
auto const err = error->code;
|
2022-03-14 04:43:35 +00:00
|
|
|
tr_logAddErrorTor(
|
|
|
|
tor,
|
|
|
|
fmt::format(
|
2022-03-16 00:51:36 +00:00
|
|
|
_("Couldn't read '{path}': {error} ({error_code})"),
|
2022-03-14 04:43:35 +00:00
|
|
|
fmt::arg("path", tor->fileSubpath(file_index)),
|
2022-03-16 00:51:36 +00:00
|
|
|
fmt::arg("error", error->message),
|
|
|
|
fmt::arg("error_code", error->code)));
|
2022-02-22 17:21:24 +00:00
|
|
|
tr_error_free(error);
|
2022-06-01 16:56:59 +00:00
|
|
|
return err;
|
2012-12-05 17:29:46 +00:00
|
|
|
}
|
2022-02-22 17:21:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IoMode::Write:
|
2022-06-01 16:56:59 +00:00
|
|
|
if (tr_error* error = nullptr; !writeEntireBuf(*fd, file_offset, buf, buflen, &error) && error != nullptr)
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2022-06-01 16:56:59 +00:00
|
|
|
auto const err = error->code;
|
2022-03-14 04:43:35 +00:00
|
|
|
tr_logAddErrorTor(
|
|
|
|
tor,
|
|
|
|
fmt::format(
|
2022-03-16 00:51:36 +00:00
|
|
|
_("Couldn't save '{path}': {error} ({error_code})"),
|
2022-03-14 04:43:35 +00:00
|
|
|
fmt::arg("path", tor->fileSubpath(file_index)),
|
2022-03-16 00:51:36 +00:00
|
|
|
fmt::arg("error", error->message),
|
|
|
|
fmt::arg("error_code", error->code)));
|
2022-02-22 17:21:24 +00:00
|
|
|
tr_error_free(error);
|
2022-06-01 16:56:59 +00:00
|
|
|
return err;
|
2009-11-25 19:26:30 +00:00
|
|
|
}
|
2022-02-22 17:21:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case IoMode::Prefetch:
|
2022-04-29 02:35:47 +00:00
|
|
|
tr_sys_file_advise(*fd, file_offset, buflen, TR_SYS_FILE_ADVICE_WILL_NEED);
|
2022-02-22 17:21:24 +00:00
|
|
|
break;
|
2009-10-19 05:05:00 +00:00
|
|
|
}
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2022-06-01 16:56:59 +00:00
|
|
|
return 0;
|
2007-05-02 19:35:34 +00:00
|
|
|
}
|
|
|
|
|
2008-10-03 04:49:06 +00:00
|
|
|
/* returns 0 on success, or an errno on failure */
|
2022-02-22 17:21:24 +00:00
|
|
|
int readOrWritePiece(tr_torrent* tor, IoMode io_mode, tr_block_info::Location loc, uint8_t* buf, size_t buflen)
|
2007-06-18 03:40:41 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
int err = 0;
|
2007-05-27 23:32:26 +00:00
|
|
|
|
2022-02-19 16:07:08 +00:00
|
|
|
if (loc.piece >= tor->pieceCount())
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return EINVAL;
|
|
|
|
}
|
2007-05-27 23:32:26 +00:00
|
|
|
|
2022-02-19 16:07:08 +00:00
|
|
|
auto [file_index, file_offset] = tor->fileOffset(loc);
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2017-05-23 19:17:17 +00:00
|
|
|
while (buflen != 0 && err == 0)
|
2006-07-16 19:39:23 +00:00
|
|
|
{
|
2022-01-02 01:25:25 +00:00
|
|
|
uint64_t const bytes_this_pass = std::min(uint64_t{ buflen }, uint64_t{ tor->fileSize(file_index) - file_offset });
|
2007-06-18 03:40:41 +00:00
|
|
|
|
2022-02-22 17:21:24 +00:00
|
|
|
err = readOrWriteBytes(tor->session, tor, io_mode, file_index, file_offset, buf, bytes_this_pass);
|
2022-01-02 01:25:25 +00:00
|
|
|
buf += bytes_this_pass;
|
|
|
|
buflen -= bytes_this_pass;
|
2009-08-13 17:25:26 +00:00
|
|
|
|
2022-02-22 17:21:24 +00:00
|
|
|
if (err != 0 && io_mode == IoMode::Write && tor->error != TR_STAT_LOCAL_ERROR)
|
2010-01-26 21:41:40 +00:00
|
|
|
{
|
2022-04-22 16:35:56 +00:00
|
|
|
auto const path = tr_pathbuf{ tor->downloadDir(), '/', tor->fileSubpath(file_index) };
|
2022-03-30 19:59:13 +00:00
|
|
|
tor->setLocalError(fmt::format(FMT_STRING("{:s} ({:s})"), tr_strerror(err), path));
|
2022-08-26 23:57:44 +00:00
|
|
|
tr_torrentStop(tor);
|
2009-08-13 17:25:26 +00:00
|
|
|
}
|
2022-01-16 18:02:47 +00:00
|
|
|
|
|
|
|
++file_index;
|
|
|
|
file_offset = 0;
|
2006-07-16 19:39:23 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
return err;
|
2006-07-16 19:39:23 +00:00
|
|
|
}
|
|
|
|
|
2022-02-22 17:21:24 +00:00
|
|
|
std::optional<tr_sha1_digest_t> recalculateHash(tr_torrent* tor, tr_piece_index_t piece)
|
2007-06-18 03:40:41 +00:00
|
|
|
{
|
2021-09-15 00:18:09 +00:00
|
|
|
TR_ASSERT(tor != nullptr);
|
2021-12-15 15:53:20 +00:00
|
|
|
TR_ASSERT(piece < tor->pieceCount());
|
2017-06-13 02:24:09 +00:00
|
|
|
|
2022-01-07 19:13:37 +00:00
|
|
|
auto bytes_left = size_t(tor->pieceSize(piece));
|
2022-02-19 16:07:08 +00:00
|
|
|
auto loc = tor->pieceLoc(piece);
|
|
|
|
tr_ioPrefetch(tor, loc, bytes_left);
|
2006-07-16 19:39:23 +00:00
|
|
|
|
2022-07-31 20:58:14 +00:00
|
|
|
auto sha = tr_sha1::create();
|
2022-02-23 20:25:06 +00:00
|
|
|
auto buffer = std::vector<uint8_t>(tr_block_info::BlockSize);
|
2021-10-29 18:24:30 +00:00
|
|
|
while (bytes_left != 0)
|
2008-09-06 13:25:21 +00:00
|
|
|
{
|
2021-10-29 18:24:30 +00:00
|
|
|
size_t const len = std::min(bytes_left, std::size(buffer));
|
2022-06-12 00:46:30 +00:00
|
|
|
if (auto const success = tor->session->cache->readBlock(tor, loc, len, std::data(buffer)) == 0; !success)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-10-29 18:24:30 +00:00
|
|
|
return {};
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
|
2022-07-31 20:58:14 +00:00
|
|
|
sha->add(std::data(buffer), len);
|
2022-02-19 16:07:08 +00:00
|
|
|
loc = tor->byteLoc(loc.byte + len);
|
2021-10-29 18:24:30 +00:00
|
|
|
bytes_left -= len;
|
2008-01-27 17:03:58 +00:00
|
|
|
}
|
2008-09-06 13:25:21 +00:00
|
|
|
|
2022-08-20 20:57:32 +00:00
|
|
|
return sha->finish();
|
2006-07-16 19:39:23 +00:00
|
|
|
}
|
|
|
|
|
2022-02-22 17:21:24 +00:00
|
|
|
} // namespace
|
|
|
|
|
2022-08-03 06:15:37 +00:00
|
|
|
int tr_ioRead(tr_torrent* tor, tr_block_info::Location loc, uint32_t len, uint8_t* setme)
|
2022-02-22 17:21:24 +00:00
|
|
|
{
|
2022-08-03 06:15:37 +00:00
|
|
|
return readOrWritePiece(tor, IoMode::Read, loc, setme, len);
|
2022-02-22 17:21:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int tr_ioPrefetch(tr_torrent* tor, tr_block_info::Location loc, uint32_t len)
|
|
|
|
{
|
|
|
|
return readOrWritePiece(tor, IoMode::Prefetch, loc, nullptr, len);
|
|
|
|
}
|
|
|
|
|
2022-08-03 06:15:37 +00:00
|
|
|
int tr_ioWrite(tr_torrent* tor, tr_block_info::Location loc, uint32_t len, uint8_t const* writeme)
|
2022-02-22 17:21:24 +00:00
|
|
|
{
|
2022-08-03 06:15:37 +00:00
|
|
|
return readOrWritePiece(tor, IoMode::Write, loc, const_cast<uint8_t*>(writeme), len);
|
2022-02-22 17:21:24 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
bool tr_ioTestPiece(tr_torrent* tor, tr_piece_index_t piece)
|
2006-07-16 19:39:23 +00:00
|
|
|
{
|
2021-10-29 18:24:30 +00:00
|
|
|
auto const hash = recalculateHash(tor, piece);
|
2021-11-12 16:42:51 +00:00
|
|
|
return hash && *hash == tor->pieceHash(piece);
|
2008-12-31 18:08:13 +00:00
|
|
|
}
|