fix: FTBFS on Windows (#4036)

* refactor: getPiececLength() now returns size_t

* refactor: tr_torrentSetMetadataPiece() length arg is now size_t

* refactor: tr_peerIo::flushOutgoingProtocolMsgs() returns size_t, takes a tr_error** for reporting errors

* refactor: define tr_mode_t for convenience

* fix: suseconds_t portability fix
This commit is contained in:
Charles Kerr 2022-10-28 19:12:37 -05:00 committed by GitHub
parent 210bb03f2f
commit cdf817f2e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 162 additions and 138 deletions

View File

@ -177,18 +177,18 @@ void tr_bandwidth::phaseOne(std::vector<tr_peerIo*>& peer_array, tr_direction di
{
int const i = tr_rand_int_weak(n); /* pick a peer at random */
/* value of 3000 bytes chosen so that when using µTP we'll send a full-size
* frame right away and leave enough buffered data for the next frame to go
* out in a timely manner. */
size_t const increment = 3000;
// value of 3000 bytes chosen so that when using µTP we'll send a full-size
// frame right away and leave enough buffered data for the next frame to go
// out in a timely manner.
static auto constexpr Increment = size_t{ 3000 };
auto const bytes_used = peer_array[i]->flush(dir, increment);
auto const bytes_used = peer_array[i]->flush(dir, Increment);
tr_logAddTrace(fmt::format("peer #{} of {} used {} bytes in this pass", i, n, bytes_used));
if (bytes_used != increment)
if (bytes_used != Increment)
{
/* peer is done writing for now; move it to the end of the list */
// peer is done writing for now; move it to the end of the list
std::swap(peer_array[i], peer_array[n - 1]);
--n;
}

View File

@ -372,7 +372,7 @@ bool tr_sys_path_copy(char const* src_path, char const* dst_path, tr_error** err
while (file_size > 0U)
{
size_t const chunk_size = std::min(file_size, uint64_t{ SSIZE_MAX });
ssize_t const copied = copy_file_range(in, nullptr, out, nullptr, chunk_size, 0);
auto const copied = copy_file_range(in, nullptr, out, nullptr, chunk_size, 0);
TR_ASSERT(copied == -1 || copied >= 0); /* -1 for error; some non-negative value otherwise. */
@ -425,7 +425,7 @@ bool tr_sys_path_copy(char const* src_path, char const* dst_path, tr_error** err
while (file_size > 0U)
{
size_t const chunk_size = std::min(file_size, uint64_t{ SSIZE_MAX });
ssize_t const copied = sendfile64(out, in, nullptr, chunk_size);
auto const copied = sendfile64(out, in, nullptr, chunk_size);
TR_ASSERT(copied == -1 || copied >= 0); /* -1 for error; some non-negative value otherwise. */
if (copied == -1)
@ -641,7 +641,7 @@ bool tr_sys_file_read(tr_sys_file_t handle, void* buffer, uint64_t size, uint64_
bool ret = false;
ssize_t const my_bytes_read = read(handle, buffer, size);
auto const my_bytes_read = read(handle, buffer, size);
static_assert(sizeof(*bytes_read) >= sizeof(my_bytes_read));
if (my_bytes_read != -1)
@ -678,7 +678,7 @@ bool tr_sys_file_read_at(
#ifdef HAVE_PREAD
ssize_t const my_bytes_read = pread(handle, buffer, size, offset);
auto const my_bytes_read = pread(handle, buffer, size, offset);
#else
@ -712,7 +712,7 @@ bool tr_sys_file_write(tr_sys_file_t handle, void const* buffer, uint64_t size,
bool ret = false;
ssize_t const my_bytes_written = write(handle, buffer, size);
auto const my_bytes_written = write(handle, buffer, size);
static_assert(sizeof(*bytes_written) >= sizeof(my_bytes_written));
if (my_bytes_written != -1)
@ -749,7 +749,7 @@ bool tr_sys_file_write_at(
#ifdef HAVE_PWRITE
ssize_t const my_bytes_written = pwrite(handle, buffer, size, offset);
auto const my_bytes_written = pwrite(handle, buffer, size, offset);
#else

View File

@ -47,6 +47,15 @@ static constexpr auto UtpReadBufferSize = 256 * 1024;
#define tr_logAddDebugIo(io, msg) tr_logAddDebug(msg, (io)->addrStr())
#define tr_logAddTraceIo(io, msg) tr_logAddTrace(msg, (io)->addrStr())
[[nodiscard]] static constexpr auto isSupportedSocket(tr_peer_socket const& sock)
{
#ifdef WITH_UTP
return sock.type == TR_PEER_SOCKET_TYPE_TCP || sock.type == TR_PEER_SOCKET_TYPE_UTP;
#else
return sock.type == TR_PEER_SOCKET_TYPE_TCP;
#endif
}
static constexpr size_t guessPacketOverhead(size_t d)
{
/**
@ -244,6 +253,13 @@ static void event_read_cb(evutil_socket_t fd, short /*event*/, void* vio)
tr_error_clear(&error);
}
// Helps us to ignore errors that say "try again later"
// since that's what peer-io does by default anyway.
[[nodiscard]] static auto constexpr canRetryFromError(int error_code)
{
return error_code == 0 || error_code == EAGAIN || error_code == EINTR || error_code == EINPROGRESS;
}
static void event_write_cb(evutil_socket_t fd, short /*event*/, void* vio)
{
auto* io = static_cast<tr_peerIo*>(vio);
@ -267,10 +283,9 @@ static void event_write_cb(evutil_socket_t fd, short /*event*/, void* vio)
return;
}
EVUTIL_SET_SOCKET_ERROR(0);
auto const n_written = io->outbuf.toSocket(fd, howmuch); // -1 on err, 0 on EOF
auto const err = EVUTIL_SOCKET_ERROR();
auto const should_retry = n_written == -1 && (err == 0 || err == EAGAIN || err == EINTR || err == EINPROGRESS);
tr_error* error = nullptr;
auto const n_written = io->outbuf.toSocket(fd, howmuch, &error);
auto const should_retry = (error == nullptr) || canRetryFromError(error->code);
// schedule another write if we have more data to write & think future writes would succeed
if (!std::empty(io->outbuf) && (n_written > 0 || should_retry))
@ -284,16 +299,24 @@ static void event_write_cb(evutil_socket_t fd, short /*event*/, void* vio)
}
else
{
auto const what = n_written == -1 ? BEV_EVENT_WRITING | BEV_EVENT_ERROR : BEV_EVENT_WRITING | BEV_EVENT_EOF;
auto const errmsg = tr_net_strerror(err);
auto const what = BEV_EVENT_WRITING | (n_written == 0 ? BEV_EVENT_EOF : BEV_EVENT_ERROR);
tr_logAddDebugIo(
io,
fmt::format("event_write_cb got an err. n_written:{}, what:{}, errno:{} ({})", n_written, what, err, errmsg));
fmt::format(
"event_write_cb got an err. n_written:{}, what:{}, errno:{} ({})",
n_written,
what,
(error != nullptr ? error->code : 0),
(error != nullptr ? error->message : "EOF")));
if (io->gotError != nullptr)
{
io->gotError(io, what, io->userData);
}
}
tr_error_clear(&error);
}
/**
@ -326,7 +349,7 @@ static size_t utp_get_rb_size(tr_peerIo* const io)
return UtpReadBufferSize - bytes;
}
static ssize_t tr_peerIoTryWrite(tr_peerIo* io, size_t howmuch);
static size_t tr_peerIoTryWrite(tr_peerIo* io, size_t howmuch, tr_error** error = nullptr);
static void utp_on_writable(tr_peerIo* io)
{
@ -460,11 +483,7 @@ std::shared_ptr<tr_peerIo> tr_peerIo::create(
TR_ASSERT(session->events != nullptr);
auto lock = session->unique_lock();
#ifdef WITH_UTP
TR_ASSERT(socket.type == TR_PEER_SOCKET_TYPE_TCP || socket.type == TR_PEER_SOCKET_TYPE_UTP);
#else
TR_ASSERT(socket.type == TR_PEER_SOCKET_TYPE_TCP);
#endif
TR_ASSERT(isSupportedSocket(socket));
TR_ASSERT(session->allowsTCP() || socket.type != TR_PEER_SOCKET_TYPE_TCP);
if (socket.type == TR_PEER_SOCKET_TYPE_TCP)
@ -875,142 +894,146 @@ void tr_peerIo::readBufferDrain(size_t byte_count)
****
***/
static ssize_t tr_peerIoTryRead(tr_peerIo* io, size_t howmuch)
static size_t tr_peerIoTryRead(tr_peerIo* io, size_t howmuch, tr_error** error)
{
auto n_read = size_t{ 0U };
howmuch = io->bandwidth().clamp(TR_DOWN, howmuch);
if (howmuch == 0)
{
return 0;
return n_read;
}
auto res = ssize_t{};
switch (io->socket.type)
TR_ASSERT(isSupportedSocket(io->socket));
if (io->socket.type == TR_PEER_SOCKET_TYPE_TCP)
{
case TR_PEER_SOCKET_TYPE_UTP:
/* UTP_RBDrained notifies libutp that your read buffer is empty.
* It opens up the congestion window by sending an ACK (soonish)
* if one was not going to be sent. */
tr_error* my_error = nullptr;
n_read = io->inbuf.addSocket(io->socket.handle.tcp, howmuch, &my_error);
if (io->readBufferSize() != 0)
{
canReadWrapper(io);
}
if (my_error != nullptr)
{
if (canRetryFromError(my_error->code))
{
tr_error_clear(&my_error);
}
else
{
short const what = BEV_EVENT_READING | BEV_EVENT_ERROR | (n_read == 0 ? BEV_EVENT_EOF : 0);
auto const msg = fmt::format(
"tr_peerIoTryRead err: res:{} what:{}, errno:{} ({})",
n_read,
what,
my_error->code,
my_error->message);
tr_logAddTraceIo(io, msg);
if (io->gotError != nullptr)
{
io->gotError(io, what, io->userData);
}
tr_error_propagate(error, &my_error);
}
}
}
#ifdef WITH_UTP
else if (io->socket.type == TR_PEER_SOCKET_TYPE_UTP)
{
// UTP_RBDrained notifies libutp that your read buffer is empty.
// It opens up the congestion window by sending an ACK (soonish)
// if one was not going to be sent.
if (io->readBufferSize() == 0)
{
utp_read_drained(io->socket.handle.utp);
}
break;
case TR_PEER_SOCKET_TYPE_TCP:
{
tr_error* error = nullptr;
res = io->inbuf.addSocket(io->socket.handle.tcp, howmuch, &error);
if (io->readBufferSize() != 0)
{
canReadWrapper(io);
}
if (error != nullptr)
{
if (error->code != EAGAIN && error->code != EINTR && error->code != EINPROGRESS && io->gotError != nullptr)
{
short what = BEV_EVENT_READING | BEV_EVENT_ERROR;
if (res == 0)
{
what |= BEV_EVENT_EOF;
}
tr_logAddTraceIo(
io,
fmt::format(
"tr_peerIoTryRead err: res:{} what:{}, errno:{} ({})",
res,
what,
error->code,
error->message));
io->gotError(io, what, io->userData);
}
tr_error_clear(&error);
}
break;
}
default:
tr_logAddDebugIo(io, fmt::format("unsupported peer socket type {}", io->socket.type));
}
#endif
return res;
return n_read;
}
static ssize_t tr_peerIoTryWrite(tr_peerIo* io, size_t howmuch)
static size_t tr_peerIoTryWrite(tr_peerIo* io, size_t howmuch, tr_error** error)
{
auto n_written = size_t{ 0U };
auto const old_len = std::size(io->outbuf);
tr_logAddTraceIo(io, fmt::format("in tr_peerIoTryWrite {}", howmuch));
howmuch = std::min(howmuch, old_len);
howmuch = io->bandwidth().clamp(TR_UP, howmuch);
if (howmuch == 0)
{
return 0;
return n_written;
}
auto n = ssize_t{};
switch (io->socket.type)
if (io->socket.type == TR_PEER_SOCKET_TYPE_TCP)
{
case TR_PEER_SOCKET_TYPE_UTP:
tr_error* my_error = nullptr;
n_written = io->outbuf.toSocket(io->socket.handle.tcp, howmuch, &my_error);
if (n_written > 0)
{
auto iov = io->outbuf.vecs(howmuch);
n = utp_writev(io->socket.handle.utp, reinterpret_cast<struct utp_iovec*>(std::data(iov)), std::size(iov));
if (n > 0)
{
io->outbuf.drain(n);
didWriteWrapper(io, n);
}
break;
didWriteWrapper(io, n_written);
}
case TR_PEER_SOCKET_TYPE_TCP:
if (my_error != nullptr)
{
EVUTIL_SET_SOCKET_ERROR(0);
n = io->outbuf.toSocket(io->socket.handle.tcp, howmuch);
int const e = EVUTIL_SOCKET_ERROR();
if (n > 0)
if (canRetryFromError(my_error->code))
{
didWriteWrapper(io, n);
tr_error_clear(&my_error);
}
if (n < 0 && io->gotError != nullptr && e != 0 && e != EPIPE && e != EAGAIN && e != EINTR && e != EINPROGRESS)
else
{
short const what = BEV_EVENT_WRITING | BEV_EVENT_ERROR;
short constexpr What = BEV_EVENT_WRITING | BEV_EVENT_ERROR;
tr_logAddTraceIo(
io,
fmt::format("tr_peerIoTryWrite err: res:{}, what:{}, errno:{} ({})", n, what, e, tr_net_strerror(e)));
io->gotError(io, what, io->userData);
fmt::format(
"tr_peerIoTryWrite err: res:{}, what:{}, errno:{} ({})",
n_written,
What,
my_error->code,
my_error->message));
io->gotError(io, What, io->userData);
tr_error_propagate(error, &my_error);
}
break;
}
default:
tr_logAddDebugIo(io, fmt::format("unsupported peer socket type {}", io->socket.type));
}
#ifdef WITH_UTP
else if (io->socket.type == TR_PEER_SOCKET_TYPE_UTP)
{
auto iov = io->outbuf.vecs(howmuch);
errno = 0;
auto const n = utp_writev(io->socket.handle.utp, reinterpret_cast<struct utp_iovec*>(std::data(iov)), std::size(iov));
auto const error_code = errno;
if (n > 0)
{
n_written = static_cast<size_t>(n);
io->outbuf.drain(n);
didWriteWrapper(io, n);
}
else if (n < 0 && !canRetryFromError(error_code))
{
tr_error_set(error, error_code, tr_strerror(error_code));
}
}
#endif
return n;
return n_written;
}
ssize_t tr_peerIo::flush(tr_direction dir, size_t limit)
size_t tr_peerIo::flush(tr_direction dir, size_t limit, tr_error** error)
{
TR_ASSERT(tr_isDirection(dir));
auto const bytes_used = dir == TR_DOWN ? tr_peerIoTryRead(this, limit) : tr_peerIoTryWrite(this, limit);
auto const bytes_used = dir == TR_DOWN ? tr_peerIoTryRead(this, limit, error) : tr_peerIoTryWrite(this, limit, error);
tr_logAddTraceIo(this, fmt::format("flushing peer-io, direction:{}, limit:{}, byte_used:{}", dir, limit, bytes_used));
return bytes_used;
}
ssize_t tr_peerIo::flushOutgoingProtocolMsgs()
size_t tr_peerIo::flushOutgoingProtocolMsgs(tr_error** error)
{
size_t byte_count = 0;
@ -1026,5 +1049,5 @@ ssize_t tr_peerIo::flushOutgoingProtocolMsgs()
byte_count += n_bytes;
}
return flush(TR_UP, byte_count);
return flush(TR_UP, byte_count, error);
}

View File

@ -144,8 +144,8 @@ public:
void readBufferAdd(void const* data, size_t n_bytes);
ssize_t flushOutgoingProtocolMsgs();
ssize_t flush(tr_direction dir, size_t byte_limit);
size_t flushOutgoingProtocolMsgs(tr_error** error = nullptr);
size_t flush(tr_direction dir, size_t byte_limit, tr_error** error = nullptr);
void writeBytes(void const* bytes, size_t n_bytes, bool is_piece_data);

View File

@ -1159,7 +1159,7 @@ static void parseUtMetadata(tr_peerMsgsImpl* msgs, uint32_t msglen)
if (msg_type == MetadataMsgType::Data && !msgs->torrent->hasMetainfo() && msg_end - benc_end <= METADATA_PIECE_SIZE &&
piece * METADATA_PIECE_SIZE + (msg_end - benc_end) <= total_size)
{
auto const piece_len = msg_end - benc_end;
size_t const piece_len = msg_end - benc_end;
tr_torrentSetMetadataPiece(msgs->torrent, piece, benc_end, piece_len);
}

View File

@ -614,7 +614,7 @@ static bool bindUnixSocket(
[[maybe_unused]] struct event_base* base,
[[maybe_unused]] struct evhttp* httpd,
[[maybe_unused]] char const* path,
[[maybe_unused]] mode_t socket_mode)
[[maybe_unused]] tr_mode_t socket_mode)
{
#ifdef _WIN32
tr_logAddError(fmt::format(
@ -642,7 +642,7 @@ static bool bindUnixSocket(
return false;
}
if (chmod(addr.sun_path, (mode_t)socket_mode) != 0)
if (chmod(addr.sun_path, socket_mode) != 0)
{
tr_logAddWarn(
fmt::format(_("Couldn't set RPC socket mode to {mode:#o}, defaulting to 0755"), fmt::arg("mode", socket_mode)));
@ -1052,7 +1052,7 @@ tr_rpc_server::tr_rpc_server(tr_session* session_in, tr_variant* settings)
}
else
{
this->socket_mode_ = static_cast<mode_t>(i);
this->socket_mode_ = static_cast<tr_mode_t>(i);
}
key = TR_KEY_rpc_bind_address;

View File

@ -141,8 +141,8 @@ public:
int anti_brute_force_limit_ = 0;
int login_attempts_ = 0;
int start_retry_counter = 0;
static mode_t constexpr DefaultRpcSocketMode = 0750;
mode_t socket_mode_ = DefaultRpcSocketMode;
static tr_mode_t constexpr DefaultRpcSocketMode = 0750;
tr_mode_t socket_mode_ = DefaultRpcSocketMode;
tr_port port_;

View File

@ -78,7 +78,7 @@ static auto constexpr DefaultPrefetchEnabled = bool{ false };
static auto constexpr DefaultCacheSizeMB = int{ 4 };
static auto constexpr DefaultPrefetchEnabled = bool{ true };
#endif
static auto constexpr DefaultUmask = int{ 022 };
static auto constexpr DefaultUmask = tr_mode_t{ 022 };
static auto constexpr SaveIntervalSecs = 360s;
static void bandwidthGroupRead(tr_session* session, std::string_view config_dir);
@ -764,13 +764,13 @@ void tr_session::setImpl(init_data& data)
if (tr_variantDictFindStrView(settings, TR_KEY_umask, &sv))
{
/* Read a umask as a string representing an octal number. */
this->umask_ = static_cast<mode_t>(tr_parseNum<uint32_t>(sv, nullptr, 8).value_or(DefaultUmask));
this->umask_ = tr_parseNum<tr_mode_t>(sv, nullptr, 8).value_or(DefaultUmask);
::umask(this->umask_);
}
else if (tr_variantDictFindInt(settings, TR_KEY_umask, &i))
{
/* Or as a base 10 integer to remain compatible with the old settings format. */
this->umask_ = (mode_t)i;
this->umask_ = static_cast<tr_mode_t>(i);
::umask(this->umask_);
}

View File

@ -987,7 +987,7 @@ private:
std::array<bool, 2> queue_enabled_ = { false, false };
std::array<size_t, 2> queue_size_ = { 0, 0 };
int umask_ = 022;
uint32_t umask_ = 022;
// One of <netinet/ip.h>'s IPTOS_ values.
// See tr_netTos*() in libtransmission/net.h for more info

View File

@ -98,7 +98,7 @@ private:
auto const secs = duration_cast<seconds>(interval_);
auto tv = timeval{};
tv.tv_sec = secs.count();
tv.tv_usec = static_cast<suseconds_t>(duration_cast<microseconds>(interval_ - secs).count());
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(duration_cast<microseconds>(interval_ - secs).count());
evtimer_add(evtimer_, &tv);
}

View File

@ -150,7 +150,7 @@ std::optional<std::vector<std::byte>> tr_torrentGetMetadataPiece(tr_torrent cons
return buf;
}
static ssize_t getPieceLength(struct tr_incomplete_metadata const* m, int piece)
static size_t getPieceLength(struct tr_incomplete_metadata const* m, int piece)
{
return piece + 1 == m->piece_count ? // last piece
std::size(m->metadata) - (piece * METADATA_PIECE_SIZE) :
@ -329,11 +329,10 @@ static void onHaveAllMetainfo(tr_torrent* tor, tr_incomplete_metadata* m)
}
}
void tr_torrentSetMetadataPiece(tr_torrent* tor, int piece, void const* data, int len)
void tr_torrentSetMetadataPiece(tr_torrent* tor, int piece, void const* data, size_t len)
{
TR_ASSERT(tr_isTorrent(tor));
TR_ASSERT(data != nullptr);
TR_ASSERT(len >= 0);
tr_logAddDebugTor(tor, fmt::format("got metadata piece {} of {} bytes", piece, len));

View File

@ -25,7 +25,7 @@ inline constexpr int METADATA_PIECE_SIZE = 1024 * 16;
std::optional<std::vector<std::byte>> tr_torrentGetMetadataPiece(tr_torrent const* tor, int piece);
void tr_torrentSetMetadataPiece(tr_torrent* tor, int piece, void const* data, int len);
void tr_torrentSetMetadataPiece(tr_torrent* tor, int piece, void const* data, size_t len);
std::optional<int> tr_torrentGetNextMetadataRequest(tr_torrent* tor, time_t now);

View File

@ -227,17 +227,18 @@ public:
drain(size());
}
// -1 on error, 0 on eof, >0 on n bytes written
auto toSocket(tr_socket_t sockfd, size_t n_bytes, tr_error** error = nullptr)
// Returns the number of bytes written. Check `error` for error.
size_t toSocket(tr_socket_t sockfd, size_t n_bytes, tr_error** error = nullptr)
{
EVUTIL_SET_SOCKET_ERROR(0);
auto const res = evbuffer_write_atmost(buf_.get(), sockfd, n_bytes);
auto const err = EVUTIL_SOCKET_ERROR();
if (res == -1)
if (res >= 0)
{
tr_error_set(error, err, tr_net_strerror(err));
return static_cast<size_t>(res);
}
return res;
tr_error_set(error, err, tr_net_strerror(err));
return 0;
}
[[nodiscard]] Iovec alloc(size_t n_bytes)

View File

@ -35,6 +35,7 @@ using tr_tracker_id_t = uint32_t;
using tr_torrent_id_t = int;
using tr_bytes_per_second_t = size_t;
using tr_kilobytes_per_second_t = size_t;
using tr_mode_t = uint16_t;
struct tr_block_span_t
{