diff --git a/Misc/NEWS.d/next/Library/2020-11-10-14-27-49.bpo-42237.F363jO.rst b/Misc/NEWS.d/next/Library/2020-11-10-14-27-49.bpo-42237.F363jO.rst new file mode 100644 index 00000000000000..50cab6e1f11f84 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-11-10-14-27-49.bpo-42237.F363jO.rst @@ -0,0 +1 @@ +Fix `os.sendfile()` on illumos. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 70b47c475feaa5..6b51d8a848eab2 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9873,11 +9873,26 @@ os_sendfile_impl(PyObject *module, int out_fd, int in_fd, PyObject *offobj, if (offset >= st.st_size) { return Py_BuildValue("i", 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 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; + } +#endif Py_END_ALLOW_THREADS } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); if (ret < 0)