Skip to content

Commit 413d955

Browse files
authored
bpo-36610: shutil.copyfile(): use sendfile() on Linux only (GH-13675)
...and avoid using it on Solaris as it can raise EINVAL if offset is equal or bigger than the size of the file
1 parent a16387a commit 413d955

File tree

5 files changed

+12
-13
lines changed

5 files changed

+12
-13
lines changed

Doc/library/shutil.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -420,8 +420,7 @@ the use of userspace buffers in Python as in "``outfd.write(infd.read())``".
420420

421421
On macOS `fcopyfile`_ is used to copy the file content (not metadata).
422422

423-
On Linux, Solaris and other POSIX platforms where :func:`os.sendfile` supports
424-
copies between 2 regular file descriptors :func:`os.sendfile` is used.
423+
On Linux :func:`os.sendfile` is used.
425424

426425
On Windows :func:`shutil.copyfile` uses a bigger default buffer size (1 MiB
427426
instead of 64 KiB) and a :func:`memoryview`-based variant of

Doc/whatsnew/3.8.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,7 @@ Optimizations
772772

773773
* :func:`shutil.copyfile`, :func:`shutil.copy`, :func:`shutil.copy2`,
774774
:func:`shutil.copytree` and :func:`shutil.move` use platform-specific
775-
"fast-copy" syscalls on Linux, macOS and Solaris in order to copy the file
775+
"fast-copy" syscalls on Linux and macOS in order to copy the file
776776
more efficiently.
777777
"fast-copy" means that the copying operation occurs within the kernel,
778778
avoiding the use of userspace buffers in Python as in

Lib/shutil.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
import nt
5151

5252
COPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 64 * 1024
53-
_HAS_SENDFILE = posix and hasattr(os, "sendfile")
53+
_USE_CP_SENDFILE = hasattr(os, "sendfile") and sys.platform.startswith("linux")
5454
_HAS_FCOPYFILE = posix and hasattr(posix, "_fcopyfile") # macOS
5555

5656
__all__ = ["copyfileobj", "copyfile", "copymode", "copystat", "copy", "copy2",
@@ -111,7 +111,7 @@ def _fastcopy_fcopyfile(fsrc, fdst, flags):
111111
def _fastcopy_sendfile(fsrc, fdst):
112112
"""Copy data from one regular mmap-like fd to another by using
113113
high-performance sendfile(2) syscall.
114-
This should work on Linux >= 2.6.33 and Solaris only.
114+
This should work on Linux >= 2.6.33 only.
115115
"""
116116
# Note: copyfileobj() is left alone in order to not introduce any
117117
# unexpected breakage. Possible risks by using zero-copy calls
@@ -122,7 +122,7 @@ def _fastcopy_sendfile(fsrc, fdst):
122122
# GzipFile (which decompresses data), HTTPResponse (which decodes
123123
# chunks).
124124
# - possibly others (e.g. encrypted fs/partition?)
125-
global _HAS_SENDFILE
125+
global _USE_CP_SENDFILE
126126
try:
127127
infd = fsrc.fileno()
128128
outfd = fdst.fileno()
@@ -152,7 +152,7 @@ def _fastcopy_sendfile(fsrc, fdst):
152152
# sendfile() on this platform (probably Linux < 2.6.33)
153153
# does not support copies between regular files (only
154154
# sockets).
155-
_HAS_SENDFILE = False
155+
_USE_CP_SENDFILE = False
156156
raise _GiveupOnFastCopy(err)
157157

158158
if err.errno == errno.ENOSPC: # filesystem is full
@@ -260,8 +260,8 @@ def copyfile(src, dst, *, follow_symlinks=True):
260260
return dst
261261
except _GiveupOnFastCopy:
262262
pass
263-
# Linux / Solaris
264-
elif _HAS_SENDFILE:
263+
# Linux
264+
elif _USE_CP_SENDFILE:
265265
try:
266266
_fastcopy_sendfile(fsrc, fdst)
267267
return dst

Lib/test/test_shutil.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2315,7 +2315,7 @@ def test_file2file_not_supported(self):
23152315
# Emulate a case where sendfile() only support file->socket
23162316
# fds. In such a case copyfile() is supposed to skip the
23172317
# fast-copy attempt from then on.
2318-
assert shutil._HAS_SENDFILE
2318+
assert shutil._USE_CP_SENDFILE
23192319
try:
23202320
with unittest.mock.patch(
23212321
self.PATCHPOINT,
@@ -2324,13 +2324,13 @@ def test_file2file_not_supported(self):
23242324
with self.assertRaises(_GiveupOnFastCopy):
23252325
shutil._fastcopy_sendfile(src, dst)
23262326
assert m.called
2327-
assert not shutil._HAS_SENDFILE
2327+
assert not shutil._USE_CP_SENDFILE
23282328

23292329
with unittest.mock.patch(self.PATCHPOINT) as m:
23302330
shutil.copyfile(TESTFN, TESTFN2)
23312331
assert not m.called
23322332
finally:
2333-
shutil._HAS_SENDFILE = True
2333+
shutil._USE_CP_SENDFILE = True
23342334

23352335

23362336
@unittest.skipIf(not MACOS, 'macOS only')

Misc/NEWS.d/3.8.0a1.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4450,7 +4450,7 @@ data_received() being called before connection_made().
44504450
44514451
:func:`shutil.copyfile`, :func:`shutil.copy`, :func:`shutil.copy2`,
44524452
:func:`shutil.copytree` and :func:`shutil.move` use platform-specific
4453-
fast-copy syscalls on Linux, Solaris and macOS in order to copy the file
4453+
fast-copy syscalls on Linux and macOS in order to copy the file
44544454
more efficiently. On Windows :func:`shutil.copyfile` uses a bigger default
44554455
buffer size (1 MiB instead of 16 KiB) and a :func:`memoryview`-based variant
44564456
of :func:`shutil.copyfileobj` is used. The speedup for copying a 512MiB file

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy