From 7daeb9d095b9acb6f96973f939c7b9a27d9627e3 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 21 Jul 2026 08:32:03 +0300 Subject: [PATCH 1/2] gh-154324: Fix os.sendfile() error reporting on Solaris illumos sendfile(3EXT) returns the number of transferred bytes by adding it to the offset without initializing it, so the offset cannot be used to detect a partial write. Use sendfilev(3EXT), which reports the number of transferred bytes in an explicit out parameter. Co-Authored-By: Claude Opus 4.8 --- ...-07-21-08-31-55.gh-issue-154324.vDxV5U.rst | 3 ++ Modules/posixmodule.c | 38 +++++++++++-------- 2 files changed, 26 insertions(+), 15 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-07-21-08-31-55.gh-issue-154324.vDxV5U.rst diff --git a/Misc/NEWS.d/next/Library/2026-07-21-08-31-55.gh-issue-154324.vDxV5U.rst b/Misc/NEWS.d/next/Library/2026-07-21-08-31-55.gh-issue-154324.vDxV5U.rst new file mode 100644 index 000000000000000..10f82d58c2f90b1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-21-08-31-55.gh-issue-154324.vDxV5U.rst @@ -0,0 +1,3 @@ +Fix :func:`os.sendfile` on Solaris and illumos: +it no longer reports a successful transfer +when the underlying system call failed without writing any data. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 75ee7e260ce9850..73cd0e61d89bd00 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -12613,27 +12613,35 @@ os_sendfile_impl(PyObject *module, int out_fd, int in_fd, PyObject *offobj, return PyLong_FromLong(0); } - // On illumos specifically sendfile() may perform a partial write but - // return -1/an error (in one confirmed case the destination socket - // had a 5 second timeout set and errno was EAGAIN) and it's on the client - // code to check if the offset parameter was modified by sendfile(). - // - // We need this variable to track said change. - off_t original_offset = offset; -#endif + // sendfile() may perform a partial write and still return -1, so the + // number of transferred bytes must be taken from the out parameter. + // sendfile() reports it by adding it to the offset, but does not + // initialize it when the transfer fails before writing any data, so use + // sendfilev(), which reports it explicitly. + sendfilevec_t vec; + size_t xferred; + + vec.sfv_fd = in_fd; + vec.sfv_flag = 0; + vec.sfv_off = offset; + vec.sfv_len = count; do { Py_BEGIN_ALLOW_THREADS - ret = sendfile(out_fd, in_fd, &offset, count); -#if defined(__sun) && defined(__SVR4) - // This handles illumos-specific sendfile() partial write behavior, - // see a comment above for more details. - if (ret < 0 && offset != original_offset) { - ret = offset - original_offset; + xferred = 0; + ret = sendfilev(out_fd, &vec, 1, &xferred); + if (ret < 0 && xferred != 0) { + ret = (Py_ssize_t)xferred; } -#endif Py_END_ALLOW_THREADS } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); +#else + do { + Py_BEGIN_ALLOW_THREADS + ret = sendfile(out_fd, in_fd, &offset, count); + Py_END_ALLOW_THREADS + } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); +#endif if (ret < 0) return (!async_err) ? posix_error() : NULL; return PyLong_FromSsize_t(ret); From 554cf23011a52aaa9a2280c9fa6543a2cfd7401a Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 22 Jul 2026 14:04:14 +0300 Subject: [PATCH 2/2] Mention illumos in the NEWS entry Oracle Solaris is not affected: its sendfile() reports the number of transferred bytes correctly. Co-Authored-By: Claude Opus 4.8 --- .../next/Library/2026-07-21-08-31-55.gh-issue-154324.vDxV5U.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2026-07-21-08-31-55.gh-issue-154324.vDxV5U.rst b/Misc/NEWS.d/next/Library/2026-07-21-08-31-55.gh-issue-154324.vDxV5U.rst index 10f82d58c2f90b1..388e4deb7c41989 100644 --- a/Misc/NEWS.d/next/Library/2026-07-21-08-31-55.gh-issue-154324.vDxV5U.rst +++ b/Misc/NEWS.d/next/Library/2026-07-21-08-31-55.gh-issue-154324.vDxV5U.rst @@ -1,3 +1,3 @@ -Fix :func:`os.sendfile` on Solaris and illumos: +Fix :func:`os.sendfile` on illumos: it no longer reports a successful transfer when the underlying system call failed without writing any data.