diff --git a/libtransmission/bandwidth.cc b/libtransmission/bandwidth.cc index 3ae3ebc37..2d7b71f0e 100644 --- a/libtransmission/bandwidth.cc +++ b/libtransmission/bandwidth.cc @@ -177,18 +177,18 @@ void tr_bandwidth::phaseOne(std::vector& 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; } diff --git a/libtransmission/file-posix.cc b/libtransmission/file-posix.cc index 8efcc0f2b..5d60c7ac7 100644 --- a/libtransmission/file-posix.cc +++ b/libtransmission/file-posix.cc @@ -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 diff --git a/libtransmission/peer-io.cc b/libtransmission/peer-io.cc index 0b134bb67..0e4b25691 100644 --- a/libtransmission/peer-io.cc +++ b/libtransmission/peer-io.cc @@ -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(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::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(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(std::data(iov)), std::size(iov)); + auto const error_code = errno; + if (n > 0) + { + n_written = static_cast(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); } diff --git a/libtransmission/peer-io.h b/libtransmission/peer-io.h index 612fb25ec..c5d489f62 100644 --- a/libtransmission/peer-io.h +++ b/libtransmission/peer-io.h @@ -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); diff --git a/libtransmission/peer-msgs.cc b/libtransmission/peer-msgs.cc index 44ca5d1b0..9d52ead02 100644 --- a/libtransmission/peer-msgs.cc +++ b/libtransmission/peer-msgs.cc @@ -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); } diff --git a/libtransmission/rpc-server.cc b/libtransmission/rpc-server.cc index 99ee90e00..dc58ddebd 100644 --- a/libtransmission/rpc-server.cc +++ b/libtransmission/rpc-server.cc @@ -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(i); + this->socket_mode_ = static_cast(i); } key = TR_KEY_rpc_bind_address; diff --git a/libtransmission/rpc-server.h b/libtransmission/rpc-server.h index 34330639f..a9626733f 100644 --- a/libtransmission/rpc-server.h +++ b/libtransmission/rpc-server.h @@ -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_; diff --git a/libtransmission/session.cc b/libtransmission/session.cc index e9e9e9a9d..376fdf15b 100644 --- a/libtransmission/session.cc +++ b/libtransmission/session.cc @@ -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(tr_parseNum(sv, nullptr, 8).value_or(DefaultUmask)); + this->umask_ = tr_parseNum(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(i); ::umask(this->umask_); } diff --git a/libtransmission/session.h b/libtransmission/session.h index 2e03400f1..fe255494d 100644 --- a/libtransmission/session.h +++ b/libtransmission/session.h @@ -987,7 +987,7 @@ private: std::array queue_enabled_ = { false, false }; std::array queue_size_ = { 0, 0 }; - int umask_ = 022; + uint32_t umask_ = 022; // One of 's IPTOS_ values. // See tr_netTos*() in libtransmission/net.h for more info diff --git a/libtransmission/timer-ev.cc b/libtransmission/timer-ev.cc index a175a5d73..13f346bc0 100644 --- a/libtransmission/timer-ev.cc +++ b/libtransmission/timer-ev.cc @@ -98,7 +98,7 @@ private: auto const secs = duration_cast(interval_); auto tv = timeval{}; tv.tv_sec = secs.count(); - tv.tv_usec = static_cast(duration_cast(interval_ - secs).count()); + tv.tv_usec = static_cast(duration_cast(interval_ - secs).count()); evtimer_add(evtimer_, &tv); } diff --git a/libtransmission/torrent-magnet.cc b/libtransmission/torrent-magnet.cc index 4eba8dac9..7d7db3941 100644 --- a/libtransmission/torrent-magnet.cc +++ b/libtransmission/torrent-magnet.cc @@ -150,7 +150,7 @@ std::optional> 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)); diff --git a/libtransmission/torrent-magnet.h b/libtransmission/torrent-magnet.h index e3e846a17..84bad145b 100644 --- a/libtransmission/torrent-magnet.h +++ b/libtransmission/torrent-magnet.h @@ -25,7 +25,7 @@ inline constexpr int METADATA_PIECE_SIZE = 1024 * 16; std::optional> 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 tr_torrentGetNextMetadataRequest(tr_torrent* tor, time_t now); diff --git a/libtransmission/tr-buffer.h b/libtransmission/tr-buffer.h index 484f84828..d6ecf147c 100644 --- a/libtransmission/tr-buffer.h +++ b/libtransmission/tr-buffer.h @@ -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(res); } - return res; + tr_error_set(error, err, tr_net_strerror(err)); + return 0; } [[nodiscard]] Iovec alloc(size_t n_bytes) diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index cbf71033c..e5957c2e5 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -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 {