fix: ensure all bytes read / written to file (#2682)

This commit is contained in:
Charles Kerr 2022-02-22 11:21:24 -06:00 committed by GitHub
parent 54d1a02e92
commit e6e1e1d220
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 96 additions and 63 deletions

View File

@ -5,11 +5,11 @@
#include <algorithm>
#include <cerrno>
#include <cstdlib> /* abort() */
#include <optional>
#include <vector>
#include "transmission.h"
#include "cache.h" /* tr_cacheReadBlock() */
#include "crypto-utils.h"
#include "error.h"
@ -17,7 +17,6 @@
#include "file.h"
#include "inout.h"
#include "log.h"
#include "peer-common.h" /* MAX_BLOCK_SIZE */
#include "stats.h" /* tr_statsFileCreated() */
#include "torrent.h"
#include "tr-assert.h"
@ -25,32 +24,68 @@
using namespace std::literals;
/****
***** Low-level IO functions
****/
enum
namespace
{
TR_IO_READ,
TR_IO_PREFETCH,
/* Any operations that require write access must follow TR_IO_WRITE. */
TR_IO_WRITE
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;
}
return true;
}
enum class IoMode
{
Read,
Prefetch,
Write
};
/* returns 0 on success, or an errno on failure */
static int readOrWriteBytes(
int readOrWriteBytes(
tr_session* session,
tr_torrent* tor,
int ioMode,
IoMode io_mode,
tr_file_index_t file_index,
uint64_t file_offset,
void* buf,
uint8_t* buf,
size_t buflen)
{
TR_ASSERT(file_index < tor->fileCount());
int err = 0;
bool const doWrite = ioMode >= TR_IO_WRITE;
bool const doWrite = io_mode == IoMode::Write;
auto const file_size = tor->fileSize(file_index);
TR_ASSERT(file_size == 0 || file_offset < file_size);
TR_ASSERT(file_offset + buflen <= file_size);
@ -108,47 +143,47 @@ static int readOrWriteBytes(
tr_free(subpath);
}
if (err != 0)
{
return err;
}
/***
**** Use the fd
***/
if (err == 0)
{
tr_error* error = nullptr;
tr_error* error = nullptr;
if (ioMode == TR_IO_READ)
switch (io_mode)
{
case IoMode::Read:
if (!readEntireBuf(fd, file_offset, buf, buflen, &error))
{
if (!tr_sys_file_read_at(fd, buf, buflen, file_offset, nullptr, &error))
{
err = error->code;
tr_logAddTorErr(tor, "read failed for \"%s\": %s", tor->fileSubpath(file_index).c_str(), error->message);
tr_error_free(error);
}
err = error->code;
tr_logAddTorErr(tor, "read failed for \"%s\": %s", tor->fileSubpath(file_index).c_str(), error->message);
tr_error_free(error);
}
else if (ioMode == TR_IO_WRITE)
break;
case IoMode::Write:
if (!writeEntireBuf(fd, file_offset, buf, buflen, &error))
{
if (!tr_sys_file_write_at(fd, buf, buflen, file_offset, nullptr, &error))
{
err = error->code;
tr_logAddTorErr(tor, "write failed for \"%s\": %s", tor->fileSubpath(file_index).c_str(), error->message);
tr_error_free(error);
}
}
else if (ioMode == TR_IO_PREFETCH)
{
tr_sys_file_advise(fd, file_offset, buflen, TR_SYS_FILE_ADVICE_WILL_NEED, nullptr);
}
else
{
abort();
err = error->code;
tr_logAddTorErr(tor, "write failed for \"%s\": %s", tor->fileSubpath(file_index).c_str(), error->message);
tr_error_free(error);
}
break;
case IoMode::Prefetch:
tr_sys_file_advise(fd, file_offset, buflen, TR_SYS_FILE_ADVICE_WILL_NEED, nullptr);
break;
}
return err;
}
/* returns 0 on success, or an errno on failure */
static int readOrWritePiece(tr_torrent* tor, int ioMode, tr_block_info::Location loc, uint8_t* buf, size_t buflen)
int readOrWritePiece(tr_torrent* tor, IoMode io_mode, tr_block_info::Location loc, uint8_t* buf, size_t buflen)
{
int err = 0;
@ -163,11 +198,11 @@ static int readOrWritePiece(tr_torrent* tor, int ioMode, tr_block_info::Location
{
uint64_t const bytes_this_pass = std::min(uint64_t{ buflen }, uint64_t{ tor->fileSize(file_index) - file_offset });
err = readOrWriteBytes(tor->session, tor, ioMode, file_index, file_offset, buf, bytes_this_pass);
err = readOrWriteBytes(tor->session, tor, io_mode, file_index, file_offset, buf, bytes_this_pass);
buf += bytes_this_pass;
buflen -= bytes_this_pass;
if (err != 0 && ioMode == TR_IO_WRITE && tor->error != TR_STAT_LOCAL_ERROR)
if (err != 0 && io_mode == IoMode::Write && tor->error != TR_STAT_LOCAL_ERROR)
{
auto const path = tr_strvPath(tor->downloadDir().sv(), tor->fileSubpath(file_index));
tor->setLocalError(tr_strvJoin(tr_strerror(err), " ("sv, path, ")"sv));
@ -180,26 +215,7 @@ static int readOrWritePiece(tr_torrent* tor, int ioMode, tr_block_info::Location
return err;
}
int tr_ioRead(tr_torrent* tor, tr_block_info::Location loc, uint32_t len, uint8_t* buf)
{
return readOrWritePiece(tor, TR_IO_READ, loc, buf, len);
}
int tr_ioPrefetch(tr_torrent* tor, tr_block_info::Location loc, uint32_t len)
{
return readOrWritePiece(tor, TR_IO_PREFETCH, loc, nullptr, len);
}
int tr_ioWrite(tr_torrent* tor, tr_block_info::Location loc, uint32_t len, uint8_t const* buf)
{
return readOrWritePiece(tor, TR_IO_WRITE, loc, (uint8_t*)buf, len);
}
/****
*****
****/
static std::optional<tr_sha1_digest_t> recalculateHash(tr_torrent* tor, tr_piece_index_t piece)
std::optional<tr_sha1_digest_t> recalculateHash(tr_torrent* tor, tr_piece_index_t piece)
{
TR_ASSERT(tor != nullptr);
TR_ASSERT(piece < tor->pieceCount());
@ -227,6 +243,23 @@ static std::optional<tr_sha1_digest_t> recalculateHash(tr_torrent* tor, tr_piece
return tr_sha1_final(sha);
}
} // namespace
int tr_ioRead(tr_torrent* tor, tr_block_info::Location loc, uint32_t len, uint8_t* buf)
{
return readOrWritePiece(tor, IoMode::Read, loc, buf, len);
}
int tr_ioPrefetch(tr_torrent* tor, tr_block_info::Location loc, uint32_t len)
{
return readOrWritePiece(tor, IoMode::Prefetch, loc, nullptr, len);
}
int tr_ioWrite(tr_torrent* tor, tr_block_info::Location loc, uint32_t len, uint8_t const* buf)
{
return readOrWritePiece(tor, IoMode::Write, loc, (uint8_t*)buf, len);
}
bool tr_ioTestPiece(tr_torrent* tor, tr_piece_index_t piece)
{
auto const hash = recalculateHash(tor, piece);