2023-11-01 21:11:11 +00:00
|
|
|
// This file Copyright © 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>
|
2023-02-14 02:57:20 +00:00
|
|
|
#include <array>
|
2021-09-19 20:41:35 +00:00
|
|
|
#include <cerrno>
|
2023-11-21 15:02:03 +00:00
|
|
|
#include <cstddef>
|
2021-10-29 18:24:30 +00:00
|
|
|
#include <optional>
|
2023-07-08 15:24:03 +00:00
|
|
|
#include <string_view>
|
|
|
|
#include <utility> // std::move
|
2007-07-29 18:11:21 +00:00
|
|
|
|
2022-03-14 04:43:35 +00:00
|
|
|
#include <fmt/core.h>
|
|
|
|
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/transmission.h"
|
2022-02-22 17:21:24 +00:00
|
|
|
|
2023-07-08 15:24:03 +00:00
|
|
|
#include "libtransmission/block-info.h" // tr_block_info
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/crypto-utils.h"
|
|
|
|
#include "libtransmission/error.h"
|
|
|
|
#include "libtransmission/file.h"
|
|
|
|
#include "libtransmission/inout.h"
|
2023-07-08 15:24:03 +00:00
|
|
|
#include "libtransmission/session.h"
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/torrent.h"
|
2023-07-08 15:24:03 +00:00
|
|
|
#include "libtransmission/torrent-files.h"
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/tr-assert.h"
|
2023-07-08 15:24:03 +00:00
|
|
|
#include "libtransmission/tr-macros.h" // tr_sha1_digest_t
|
|
|
|
#include "libtransmission/tr-strbuf.h" // tr_pathbuf
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/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
|
|
|
|
{
|
|
|
|
|
2023-12-02 20:16:36 +00:00
|
|
|
bool read_entire_buf(tr_sys_file_t const fd, uint64_t file_offset, uint8_t* buf, uint64_t buflen, tr_error& error)
|
2022-02-22 17:21:24 +00:00
|
|
|
{
|
2023-11-12 03:09:23 +00:00
|
|
|
while (buflen > 0U)
|
2022-02-22 17:21:24 +00:00
|
|
|
{
|
|
|
|
auto n_read = uint64_t{};
|
|
|
|
|
2023-12-02 20:16:36 +00:00
|
|
|
if (!tr_sys_file_read_at(fd, buf, buflen, file_offset, &n_read, &error))
|
2022-02-22 17:21:24 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf += n_read;
|
|
|
|
buflen -= n_read;
|
|
|
|
file_offset += n_read;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-12-02 20:16:36 +00:00
|
|
|
bool write_entire_buf(tr_sys_file_t const fd, uint64_t file_offset, uint8_t const* buf, uint64_t buflen, tr_error& error)
|
2022-02-22 17:21:24 +00:00
|
|
|
{
|
2023-11-12 03:09:23 +00:00
|
|
|
while (buflen > 0U)
|
2022-02-22 17:21:24 +00:00
|
|
|
{
|
|
|
|
auto n_written = uint64_t{};
|
|
|
|
|
2023-12-02 20:16:36 +00:00
|
|
|
if (!tr_sys_file_write_at(fd, buf, buflen, file_offset, &n_written, &error))
|
2022-02-22 17:21:24 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-12-02 20:16:36 +00:00
|
|
|
[[nodiscard]] std::optional<tr_sys_file_t> get_fd(
|
|
|
|
tr_session& session,
|
|
|
|
tr_open_files& open_files,
|
|
|
|
tr_torrent const& tor,
|
|
|
|
bool const writable,
|
|
|
|
tr_file_index_t const file_index,
|
|
|
|
tr_error& error)
|
2012-12-05 17:29:46 +00:00
|
|
|
{
|
2023-12-02 20:16:36 +00:00
|
|
|
auto const tor_id = tor.id();
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2023-12-02 20:16:36 +00:00
|
|
|
// is the file already open in the fd pool?
|
|
|
|
if (auto const fd = open_files.get(tor_id, file_index, writable); fd)
|
2022-06-01 16:56:59 +00:00
|
|
|
{
|
2023-12-02 20:16:36 +00:00
|
|
|
return fd;
|
2022-06-01 16:56:59 +00:00
|
|
|
}
|
|
|
|
|
2023-12-02 20:16:36 +00:00
|
|
|
// does the file exist?
|
|
|
|
auto const file_size = tor.file_size(file_index);
|
2023-12-06 15:23:16 +00:00
|
|
|
auto const prealloc = writable && tor.file_is_wanted(file_index) ? session.preallocationMode() :
|
|
|
|
tr_open_files::Preallocation::None;
|
2023-12-02 20:16:36 +00:00
|
|
|
if (auto const found = tor.find_file(file_index); found)
|
2022-06-01 16:56:59 +00:00
|
|
|
{
|
2023-12-02 20:16:36 +00:00
|
|
|
return open_files.get(tor_id, file_index, writable, found->filename(), prealloc, file_size);
|
2022-06-01 16:56:59 +00:00
|
|
|
}
|
|
|
|
|
2023-12-02 20:16:36 +00:00
|
|
|
// do we want to create it?
|
|
|
|
auto err = ENOENT;
|
2023-12-06 15:23:16 +00:00
|
|
|
if (writable)
|
2023-12-02 20:16:36 +00:00
|
|
|
{
|
|
|
|
auto const base = tor.current_dir();
|
|
|
|
auto const suffix = session.isIncompleteFileNamingEnabled() ? tr_torrent_files::PartialFileSuffix : ""sv;
|
|
|
|
auto const filename = tr_pathbuf{ base, '/', tor.file_subpath(file_index), suffix };
|
|
|
|
if (auto const fd = open_files.get(tor_id, file_index, writable, filename, prealloc, file_size); fd)
|
|
|
|
{
|
|
|
|
// make a note that we just created a file
|
|
|
|
session.add_file_created();
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
error.set(
|
|
|
|
err,
|
|
|
|
fmt::format(
|
|
|
|
_("Couldn't get '{path}': {error} ({error_code})"),
|
|
|
|
fmt::arg("path", tor.file_subpath(file_index)),
|
|
|
|
fmt::arg("error", tr_strerror(err)),
|
|
|
|
fmt::arg("error_code", err)));
|
|
|
|
return {};
|
2022-06-01 16:56:59 +00:00
|
|
|
}
|
|
|
|
|
2023-12-02 20:16:36 +00:00
|
|
|
void read_or_write_bytes(
|
|
|
|
tr_session& session,
|
|
|
|
tr_open_files& open_files,
|
|
|
|
tr_torrent const& tor,
|
2023-12-04 17:45:37 +00:00
|
|
|
bool const writable,
|
2023-12-02 20:16:36 +00:00
|
|
|
tr_file_index_t const file_index,
|
|
|
|
uint64_t const file_offset,
|
|
|
|
uint8_t* const buf,
|
|
|
|
uint64_t const buflen,
|
|
|
|
tr_error& error)
|
|
|
|
{
|
|
|
|
TR_ASSERT(file_index < tor.file_count());
|
|
|
|
auto const file_size = tor.file_size(file_index);
|
2023-11-12 03:09:23 +00:00
|
|
|
TR_ASSERT(file_size == 0U || file_offset < file_size);
|
2022-01-02 01:25:25 +00:00
|
|
|
TR_ASSERT(file_offset + buflen <= file_size);
|
2023-11-12 03:09:23 +00:00
|
|
|
if (file_size == 0U)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-03-13 21:20:39 +00:00
|
|
|
return;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2008-01-18 19:13:32 +00:00
|
|
|
|
2023-12-02 20:16:36 +00:00
|
|
|
auto const fd = get_fd(session, open_files, tor, writable, file_index, error);
|
|
|
|
if (!fd || error)
|
2022-06-11 19:35:08 +00:00
|
|
|
{
|
2023-03-13 21:20:39 +00:00
|
|
|
return;
|
2022-06-11 19:35:08 +00:00
|
|
|
}
|
|
|
|
|
2023-12-02 20:16:36 +00:00
|
|
|
auto fmtstr = ""sv;
|
2023-12-04 17:45:37 +00:00
|
|
|
if (writable)
|
2022-02-22 17:21:24 +00:00
|
|
|
{
|
2023-12-02 20:16:36 +00:00
|
|
|
fmtstr = _("Couldn't save '{path}': {error} ({error_code})");
|
|
|
|
write_entire_buf(*fd, file_offset, buf, buflen, error);
|
2023-12-04 17:45:37 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-12-02 20:16:36 +00:00
|
|
|
fmtstr = _("Couldn't read '{path}': {error} ({error_code})");
|
|
|
|
read_entire_buf(*fd, file_offset, buf, buflen, error);
|
2009-10-19 05:05:00 +00:00
|
|
|
}
|
2023-12-02 20:16:36 +00:00
|
|
|
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
tr_logAddErrorTor(
|
|
|
|
&tor,
|
|
|
|
fmt::format(
|
|
|
|
fmt::runtime(fmtstr),
|
|
|
|
fmt::arg("path", tor.file_subpath(file_index)),
|
|
|
|
fmt::arg("error", error.message()),
|
|
|
|
fmt::arg("error_code", error.code())));
|
|
|
|
}
|
2007-05-02 19:35:34 +00:00
|
|
|
}
|
|
|
|
|
2023-12-02 20:16:36 +00:00
|
|
|
void read_or_write_piece(
|
|
|
|
tr_torrent const& tor,
|
2023-12-04 17:45:37 +00:00
|
|
|
bool const writable,
|
2023-12-02 20:16:36 +00:00
|
|
|
tr_block_info::Location const loc,
|
|
|
|
uint8_t* buf,
|
|
|
|
uint64_t buflen,
|
|
|
|
tr_error& error)
|
2007-06-18 03:40:41 +00:00
|
|
|
{
|
2023-12-02 20:16:36 +00:00
|
|
|
if (loc.piece >= tor.piece_count())
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2023-12-02 20:16:36 +00:00
|
|
|
error.set_from_errno(EINVAL);
|
|
|
|
return;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2007-05-27 23:32:26 +00:00
|
|
|
|
2023-12-02 20:16:36 +00:00
|
|
|
auto [file_index, file_offset] = tor.file_offset(loc);
|
|
|
|
auto& session = *tor.session;
|
|
|
|
auto& open_files = session.openFiles();
|
|
|
|
while (buflen != 0U && !error)
|
2006-07-16 19:39:23 +00:00
|
|
|
{
|
2023-12-02 20:16:36 +00:00
|
|
|
auto const bytes_this_pass = std::min(buflen, tor.file_size(file_index) - file_offset);
|
2023-12-04 17:45:37 +00:00
|
|
|
read_or_write_bytes(session, open_files, tor, writable, file_index, file_offset, buf, bytes_this_pass, error);
|
2023-03-13 21:20:39 +00:00
|
|
|
if (buf != nullptr)
|
2010-01-26 21:41:40 +00:00
|
|
|
{
|
2023-03-13 21:20:39 +00:00
|
|
|
buf += bytes_this_pass;
|
2009-08-13 17:25:26 +00:00
|
|
|
}
|
2023-03-13 21:20:39 +00:00
|
|
|
buflen -= bytes_this_pass;
|
2022-01-16 18:02:47 +00:00
|
|
|
++file_index;
|
2023-11-12 03:09:23 +00:00
|
|
|
file_offset = 0U;
|
2006-07-16 19:39:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-02 20:16:36 +00:00
|
|
|
std::optional<tr_sha1_digest_t> recalculate_hash(tr_torrent const& tor, tr_piece_index_t const piece)
|
2007-06-18 03:40:41 +00:00
|
|
|
{
|
2023-12-02 20:16:36 +00:00
|
|
|
TR_ASSERT(piece < tor.piece_count());
|
2017-06-13 02:24:09 +00:00
|
|
|
|
2022-07-31 20:58:14 +00:00
|
|
|
auto sha = tr_sha1::create();
|
2023-02-14 02:57:20 +00:00
|
|
|
auto buffer = std::array<uint8_t, tr_block_info::BlockSize>{};
|
|
|
|
|
2023-12-02 20:16:36 +00:00
|
|
|
auto& cache = tor.session->cache;
|
|
|
|
auto const [begin_byte, end_byte] = tor.block_info().byte_span_for_piece(piece);
|
|
|
|
auto const [begin_block, end_block] = tor.block_span_for_piece(piece);
|
2023-07-01 15:22:15 +00:00
|
|
|
[[maybe_unused]] auto n_bytes_checked = size_t{};
|
2023-02-14 02:57:20 +00:00
|
|
|
for (auto block = begin_block; block < end_block; ++block)
|
2008-09-06 13:25:21 +00:00
|
|
|
{
|
2023-12-02 20:16:36 +00:00
|
|
|
auto const block_loc = tor.block_loc(block);
|
|
|
|
auto const block_len = tor.block_size(block);
|
2023-05-06 04:11:05 +00:00
|
|
|
if (auto const success = cache->read_block(tor, block_loc, block_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
|
|
|
}
|
|
|
|
|
2023-02-14 02:57:20 +00:00
|
|
|
auto begin = std::data(buffer);
|
|
|
|
auto end = begin + block_len;
|
|
|
|
|
|
|
|
// handle edge case where blocks aren't on piece boundaries:
|
|
|
|
if (block == begin_block) // `block` may begin before `piece` does
|
|
|
|
{
|
|
|
|
begin += (begin_byte - block_loc.byte);
|
|
|
|
}
|
2023-11-12 03:09:23 +00:00
|
|
|
if (block + 1U == end_block) // `block` may end after `piece` does
|
2023-02-14 02:57:20 +00:00
|
|
|
{
|
|
|
|
end -= (block_loc.byte + block_len - end_byte);
|
|
|
|
}
|
|
|
|
|
|
|
|
sha->add(begin, end - begin);
|
|
|
|
n_bytes_checked += (end - begin);
|
2008-01-27 17:03:58 +00:00
|
|
|
}
|
2008-09-06 13:25:21 +00:00
|
|
|
|
2023-12-02 20:16:36 +00:00
|
|
|
TR_ASSERT(tor.piece_size(piece) == n_bytes_checked);
|
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
|
|
|
|
|
2023-12-02 20:16:36 +00:00
|
|
|
int tr_ioRead(tr_torrent const& tor, tr_block_info::Location const& loc, size_t const len, uint8_t* const setme)
|
2022-02-22 17:21:24 +00:00
|
|
|
{
|
2023-12-02 20:16:36 +00:00
|
|
|
auto error = tr_error{};
|
2023-12-04 17:45:37 +00:00
|
|
|
read_or_write_piece(tor, false /*writable*/, loc, setme, len, error);
|
2023-12-02 20:16:36 +00:00
|
|
|
return error.code();
|
2022-02-22 17:21:24 +00:00
|
|
|
}
|
|
|
|
|
2023-12-02 20:16:36 +00:00
|
|
|
int tr_ioWrite(tr_torrent& tor, tr_block_info::Location const& loc, size_t const len, uint8_t const* const writeme)
|
2022-02-22 17:21:24 +00:00
|
|
|
{
|
2023-12-02 20:16:36 +00:00
|
|
|
auto error = tr_error{};
|
2023-12-04 17:45:37 +00:00
|
|
|
read_or_write_piece(tor, true /*writable*/, loc, const_cast<uint8_t*>(writeme), len, error);
|
2023-12-02 20:16:36 +00:00
|
|
|
|
|
|
|
// if IO failed, set torrent's error if not already set
|
|
|
|
if (error && tor.error().error_type() != TR_STAT_LOCAL_ERROR)
|
|
|
|
{
|
|
|
|
tor.error().set_local_error(error.message());
|
|
|
|
tr_torrentStop(&tor);
|
|
|
|
}
|
|
|
|
|
|
|
|
return error.code();
|
2022-02-22 17:21:24 +00:00
|
|
|
}
|
|
|
|
|
2023-12-02 20:16:36 +00:00
|
|
|
bool tr_ioTestPiece(tr_torrent const& tor, tr_piece_index_t const piece)
|
2006-07-16 19:39:23 +00:00
|
|
|
{
|
2023-12-02 20:16:36 +00:00
|
|
|
auto const hash = recalculate_hash(tor, piece);
|
|
|
|
return hash && *hash == tor.piece_hash(piece);
|
2008-12-31 18:08:13 +00:00
|
|
|
}
|