From nobody Mon Jun 29 15:52:13 2026 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4gprPn4b3hz6jNWT for ; Mon, 29 Jun 2026 15:52:13 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4gprPn2vgfz3L8j for ; Mon, 29 Jun 2026 15:52:13 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1782748333; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=zGBvZmpFZlXhvdgiC/VCFyua+J1TO3LWFztSa92A/MA=; b=aPZ5jnibVomGSxnJtSsqpUDhdNZm230Nk+1cTSZiK0HSAbW1mHSrqsjuj6La25K3F6IP0S eKdVO0u03c1b/S7CdSClSFpWIl76J0Lp2dambruZQ6T6FPb7qTTODNXCZEJ2hx7OHsj7qK JDAiTIVQoSIJwcnKwGz/VFZCtiEf1KdlAQs7t+W2OLEAGUcDxm+vXtEvptgi8HySQlZF2s vPebAqz8A4TZ0SqPAK5aB2DT0afa2LVH/LzBUWJLUPvrnb5yj6vGqoG9Ww2Mk5uRMmjFS4 Vr9+InyM/rWFBx6ZiTRYJqzxjcYgLbWLms4cJfeihd5ftTL4T6TNZo1VwJ4ZsA== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1782748333; a=rsa-sha256; cv=none; b=CYrPIeE2l8yZRoe9fSAWTFqcInGtkyPEftWC6GVyLoA5K8ZMjJgtLoxhWZm5xg8MPelPr1 7Lx6ASK7yPV8kMJuwPfNJPLc9OywvgfZiMqm/uwbplcUayp4uKluQVheLnKj/ub7TpSN6m w6pFFo7LARuu38FqKDciKGjjKIaXfPoM3QquLcgyZBPoi3Gcw8uagZZSVtiJ/FborgZf0H SA5tRJH/jSDTzN57eLVkLwgYcMZi6qLZvBuJqYueNeYz2ZPwTroeMYr0RhBitGh0nTgTGH x51QzPA2eVAgn7fQdpsgXL+0ONBicw66Br2Dk8yM3uRDgr5ra2xtZxN/2C+/qw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1782748333; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=zGBvZmpFZlXhvdgiC/VCFyua+J1TO3LWFztSa92A/MA=; b=IXF4T/5SQRcP1GB2A87Qk1YyRETjwxvpI594nbY1R031nb/Z0vkve9o+l5B/sRrdQ17IlU N6Es1F/FhWZOeR+MaNR0cHXFvdzY6FlJMzsC0nZEsRE8Kb0V5p1KPh1EiLcIkNEntkIeyD 0S18qg66BvfRHQX2qg6hiVnqJm6S6LudXeTzZ/PgKqVPJ+rA1GmEDigXv4tOxGoGFm/BXa 0DG2hWvobl5GLIilV0lguyJ1gF40tjcXmDNFBCxbp1LIKD4ldcYI2uXGeR4qm4iJmNXNc6 T/kqXP5Q7EJn0tRAyC4Uc8vbqukRIeYhPz9uVoMNaXsvTcJome5SnRzb+rKE8Q== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gprPn1jqkz13dM for ; Mon, 29 Jun 2026 15:52:13 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 3c153 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Mon, 29 Jun 2026 15:52:13 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Dag-Erling=?utf-8?Q? Sm=C3=B8rg?=rav Subject: git: 298f37a71ae6 - main - libfetch: Overhaul socket read / write List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: des X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 298f37a71ae6d9f2cb0c2abd4ff2887e81e0942c Auto-Submitted: auto-generated Date: Mon, 29 Jun 2026 15:52:13 +0000 Message-Id: <6a4294ad.3c153.42c0ca4e@gitrepo.freebsd.org> The branch main has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=298f37a71ae6d9f2cb0c2abd4ff2887e81e0942c commit 298f37a71ae6d9f2cb0c2abd4ff2887e81e0942c Author: Dag-Erling Smørgrav AuthorDate: 2026-06-27 12:16:31 +0000 Commit: Dag-Erling Smørgrav CommitDate: 2026-06-29 13:49:19 +0000 libfetch: Overhaul socket read / write * Make fetch_ssl_read() and fetch_ssl_write() behave more like read(2) and write(2), and drop fetch_socket_read() in favor of read(2). * Don't request POLLERR, it's implied. * Don't needlessly set errno, it's relatively costly. * Always check for EAGAIN from writev(2), otherwise we will abort on a short write instead of proceeding to poll(2). * Always check for EAGAIN from poll(2) even though it can't happen on FreeBSD; POSIX says it can, and it might in the future. * Rewrite fetch_read() and fetch_writev() to be more similar to each other. The main difference is that a partial read is treated as success while a partial write is treated as failure. PR: 296316 MFC after: 1 week --- lib/libfetch/common.c | 206 ++++++++++++++++++++++++++++++-------------------- lib/libfetch/common.h | 4 +- 2 files changed, 127 insertions(+), 83 deletions(-) diff --git a/lib/libfetch/common.c b/lib/libfetch/common.c index 88b5e5a2f096..c5fb45f70da2 100644 --- a/lib/libfetch/common.c +++ b/lib/libfetch/common.c @@ -1253,13 +1253,9 @@ fetch_ssl(conn_t *conn, const struct url *URL, int verbose) #endif } -#define FETCH_READ_WAIT -2 -#define FETCH_READ_ERROR -1 -#define FETCH_READ_DONE 0 - #ifdef WITH_SSL static ssize_t -fetch_ssl_read(SSL *ssl, char *buf, size_t len) +fetch_ssl_read(SSL *ssl, void *buf, size_t len) { ssize_t rlen; int ssl_err; @@ -1267,43 +1263,57 @@ fetch_ssl_read(SSL *ssl, char *buf, size_t len) rlen = SSL_read(ssl, buf, len); if (rlen < 0) { ssl_err = SSL_get_error(ssl, rlen); - if (ssl_err == SSL_ERROR_WANT_READ || - ssl_err == SSL_ERROR_WANT_WRITE) { - return (FETCH_READ_WAIT); - } else { - ERR_print_errors_fp(stderr); - return (FETCH_READ_ERROR); + switch (ssl_err) { + case SSL_ERROR_ZERO_RETURN: + return (0); + case SSL_ERROR_WANT_READ: + errno = EAGAIN; + return (-1); + case SSL_ERROR_SYSCALL: + return (-1); + default: + errno = EPROTO; + return (-1); } } return (rlen); } -#endif static ssize_t -fetch_socket_read(int sd, char *buf, size_t len) +fetch_ssl_write(SSL *ssl, void *buf, size_t len) { - ssize_t rlen; + ssize_t wlen; + int ssl_err; - rlen = read(sd, buf, len); - if (rlen < 0) { - if (errno == EAGAIN || (errno == EINTR && fetchRestartCalls)) { - return (FETCH_READ_WAIT); - } else { - return (FETCH_READ_ERROR); + wlen = SSL_write(ssl, buf, len); + if (wlen < 0) { + ssl_err = SSL_get_error(ssl, wlen); + switch (ssl_err) { + case SSL_ERROR_ZERO_RETURN: + return (0); + case SSL_ERROR_WANT_WRITE: + errno = EAGAIN; + return (-1); + case SSL_ERROR_SYSCALL: + return (-1); + default: + errno = EPROTO; + return (-1); } } - return (rlen); + return (wlen); } +#endif /* - * Read a character from a connection w/ timeout + * Read from a connection w/ timeout */ ssize_t -fetch_read(conn_t *conn, char *buf, size_t len) +fetch_read(conn_t *conn, void *buf, size_t len) { struct timeval now, timeout, delta; struct pollfd pfd; - ssize_t rlen; + ssize_t rlen, total; int deltams; if (fetchTimeout > 0) { @@ -1312,10 +1322,10 @@ fetch_read(conn_t *conn, char *buf, size_t len) } deltams = INFTIM; - memset(&pfd, 0, sizeof pfd); pfd.fd = conn->sd; - pfd.events = POLLIN | POLLERR; + pfd.events = POLLIN; + total = 0; for (;;) { /* * The socket is non-blocking. Instead of the canonical @@ -1323,8 +1333,8 @@ fetch_read(conn_t *conn, char *buf, size_t len) * * 1) call read() or SSL_read(). * 2) if we received some data, return it. - * 3) if an error occurred, return -1. - * 4) if read() or SSL_read() signaled EOF, return. + * 3) if read() or SSL_read() signaled EOF, return. + * 4) if an error occurred, return -1. * 5) if we did not receive any data but we're not at EOF, * call poll(). * @@ -1341,14 +1351,26 @@ fetch_read(conn_t *conn, char *buf, size_t len) rlen = fetch_ssl_read(conn->ssl, buf, len); else #endif - rlen = fetch_socket_read(conn->sd, buf, len); - if (rlen >= 0) { + rlen = read(conn->sd, buf, len); + if (rlen > 0) { + /* something was read */ + total += rlen; + len -= rlen; + if (len == 0) + break; + /* a partial read is success */ + break; + } else if (rlen == 0) { + /* connection closed */ break; - } else if (rlen == FETCH_READ_ERROR) { + } else if (errno != EAGAIN) { + /* error */ + if (errno == EINTR && fetchRestartCalls) + continue; fetch_syserr(); - return (-1); + break; } - // assert(rlen == FETCH_READ_WAIT); + /* check what's left of our timeout */ if (fetchTimeout > 0) { gettimeofday(&now, NULL); if (!timercmp(&timeout, &now, >)) { @@ -1360,16 +1382,19 @@ fetch_read(conn_t *conn, char *buf, size_t len) deltams = delta.tv_sec * 1000 + delta.tv_usec / 1000; } - errno = 0; - pfd.revents = 0; + /* wait for the socket to become readable */ if (poll(&pfd, 1, deltams) < 0) { + if (errno == EAGAIN) + continue; if (errno == EINTR && fetchRestartCalls) continue; - fetch_syserr(); - return (-1); + break; } } - return (rlen); + /* a partial read is success */ + if (rlen < 0 && total == 0) + return (-1); + return (total); } @@ -1426,7 +1451,7 @@ fetch_getln(conn_t *conn) * Write to a connection w/ timeout */ ssize_t -fetch_write(conn_t *conn, const char *buf, size_t len) +fetch_write(conn_t *conn, const void *buf, size_t len) { struct iovec iov; @@ -1447,17 +1472,65 @@ fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt) ssize_t wlen, total; int deltams; - memset(&pfd, 0, sizeof pfd); - if (fetchTimeout) { - pfd.fd = conn->sd; - pfd.events = POLLOUT | POLLERR; + if (fetchTimeout > 0) { gettimeofday(&timeout, NULL); timeout.tv_sec += fetchTimeout; } + deltams = INFTIM; + pfd.fd = conn->sd; + pfd.events = POLLOUT; + total = 0; - while (iovcnt > 0) { - while (fetchTimeout && pfd.revents == 0) { + for (;;) { + /* + * The socket is non-blocking. Instead of the canonical + * poll() -> write(), we do the following: + * + * 1) call write() or SSL_write(). + * 2) if we wrote everything, return success. + * 3) if write() or SSL_write() signaled EOF before we + * wrote everything, return -1. + * 4) if an error occurred, return -1. + * 5) if we did not write everything but we're not at EOF, + * call poll(). + */ +#ifdef WITH_SSL + if (conn->ssl != NULL) { + wlen = fetch_ssl_write(conn->ssl, + iov->iov_base, iov->iov_len); + } else +#endif + wlen = writev(conn->sd, iov, iovcnt); + if (wlen > 0) { + /* something was written */ + total += wlen; + /* skip iovs which were completely written */ + while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) { + wlen -= iov->iov_len; + iov++; + iovcnt--; + } + /* are we done? */ + if (iovcnt == 0) + break; + /* skip written portion of current iov */ + iov->iov_len -= wlen; + iov->iov_base = __DECONST(char *, iov->iov_base) + wlen; + /* a partial write is incomplete */ + continue; + } else if (wlen == 0) { + /* connection closed */ + break; + } else if (errno != EAGAIN) { + /* error */ + if (errno == EINTR && fetchRestartCalls) + continue; + fetch_syserr(); + break; + } + /* check what's left of our timeout */ + if (fetchTimeout > 0) { gettimeofday(&now, NULL); if (!timercmp(&timeout, &now, >)) { errno = ETIMEDOUT; @@ -1467,48 +1540,19 @@ fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt) timersub(&timeout, &now, &delta); deltams = delta.tv_sec * 1000 + delta.tv_usec / 1000; - errno = 0; - pfd.revents = 0; - if (poll(&pfd, 1, deltams) < 0) { - /* POSIX compliance */ - if (errno == EAGAIN) - continue; - if (errno == EINTR && fetchRestartCalls) - continue; - return (-1); - } } - errno = 0; -#ifdef WITH_SSL - if (conn->ssl != NULL) - wlen = SSL_write(conn->ssl, - iov->iov_base, iov->iov_len); - else -#endif - wlen = writev(conn->sd, iov, iovcnt); - if (wlen == 0) { - /* we consider a short write a failure */ - /* XXX perhaps we shouldn't in the SSL case */ - errno = EPIPE; - fetch_syserr(); - return (-1); - } - if (wlen < 0) { + /* wait for the socket to become writeable */ + if (poll(&pfd, 1, deltams) < 0) { + if (errno == EAGAIN) + continue; if (errno == EINTR && fetchRestartCalls) continue; return (-1); } - total += wlen; - while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) { - wlen -= iov->iov_len; - iov++; - iovcnt--; - } - if (iovcnt > 0) { - iov->iov_len -= wlen; - iov->iov_base = __DECONST(char *, iov->iov_base) + wlen; - } } + /* a partial write is failure */ + if (iovcnt > 0) + return (-1); return (total); } diff --git a/lib/libfetch/common.h b/lib/libfetch/common.h index 06089aae5451..479c5e03c0d1 100644 --- a/lib/libfetch/common.h +++ b/lib/libfetch/common.h @@ -119,9 +119,9 @@ conn_t *fetch_ref(conn_t *); int fetch_ssl_cb_verify_crt(int, X509_STORE_CTX*); #endif int fetch_ssl(conn_t *, const struct url *, int); -ssize_t fetch_read(conn_t *, char *, size_t); +ssize_t fetch_read(conn_t *, void *, size_t); int fetch_getln(conn_t *); -ssize_t fetch_write(conn_t *, const char *, size_t); +ssize_t fetch_write(conn_t *, const void *, size_t); ssize_t fetch_writev(conn_t *, struct iovec *, int); int fetch_putln(conn_t *, const char *, size_t); int fetch_close(conn_t *);