https://github.com/python/cpython/commit/ffdd751e6261dc2b3f9103e3cf4b90af29f52c48
commit: ffdd751e6261dc2b3f9103e3cf4b90af29f52c48
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-07-28T17:41:30Z
summary:

[3.14] gh-154324: Fix os.sendfile() error reporting on illumos (GH-154327) 
(GH-154833)

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.
(cherry picked from commit e3634bb44f7f1395dcb1b7ab3736811e9e2a4589)

Co-authored-by: Serhiy Storchaka <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-07-21-08-31-55.gh-issue-154324.vDxV5U.rst
M Modules/posixmodule.c

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 00000000000000..388e4deb7c4198
--- /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 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 26e62a1a7c50dc..7bf8bc9e5d9049 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -12095,27 +12095,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);

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to