From 6b07ef94d47d411ad4af06bc6b8868559324e705 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 21 Jan 2023 18:34:30 -0800 Subject: [PATCH] gh-87909: avoid a 0 bytes copy call to sendfile() That triggers an EAGAIN error on a minority of kernels and filesystems. --- Lib/shutil.py | 8 ++++++-- .../Library/2023-01-21-18-31-09.gh-issue-87909.bn7V1p.rst | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-01-21-18-31-09.gh-issue-87909.bn7V1p.rst diff --git a/Lib/shutil.py b/Lib/shutil.py index 867925aa10cc04..bc3fa8a5e5128d 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -130,8 +130,10 @@ def _fastcopy_sendfile(fsrc, fdst): # should not make any difference, also in case the file content # changes while being copied. try: - blocksize = max(os.fstat(infd).st_size, 2 ** 23) # min 8MiB + filesize = os.fstat(infd).st_size + blocksize = max(filesize, 2 ** 23) # min 8MiB except OSError: + filesize = float("inf") blocksize = 2 ** 27 # 128MiB # On 32-bit architectures truncate to 1GiB to avoid OverflowError, # see bpo-38319. @@ -139,7 +141,9 @@ def _fastcopy_sendfile(fsrc, fdst): blocksize = min(blocksize, 2 ** 30) offset = 0 - while True: + # We explicitly limit ourselves to filesize to avoid odd behavior + # on some systems. https://github.com/python/cpython/issues/87909 + while offset < filesize: try: sent = os.sendfile(outfd, infd, offset, blocksize) except OSError as err: diff --git a/Misc/NEWS.d/next/Library/2023-01-21-18-31-09.gh-issue-87909.bn7V1p.rst b/Misc/NEWS.d/next/Library/2023-01-21-18-31-09.gh-issue-87909.bn7V1p.rst new file mode 100644 index 00000000000000..0534f6d2e3f5cf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-01-21-18-31-09.gh-issue-87909.bn7V1p.rst @@ -0,0 +1,3 @@ +Avoid an unusual case in :func:`shutil.copyfile` where the ``sendfile`` +system call might return an error on some kernels and filesystems after all +data was transferred.