Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Lib/shutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,11 @@ def copyfile(src, dst, *, follow_symlinks=True):
if _samefile(src, dst):
raise SameFileError("{!r} and {!r} are the same file".format(src, dst))

copy_symlink = not follow_symlinks and _islink(src)
file_size = 0
for i, fn in enumerate([src, dst]):
if copy_symlink and i == 0:
continue
try:
st = _stat(fn)
except OSError:
Expand All @@ -315,7 +318,7 @@ def copyfile(src, dst, *, follow_symlinks=True):
if _WINDOWS and i == 0:
file_size = st.st_size

if not follow_symlinks and _islink(src):
if copy_symlink:
os.symlink(os.readlink(src), dst)
else:
with open(src, 'rb') as fsrc:
Expand Down
13 changes: 13 additions & 0 deletions Lib/test/test_shutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -1583,6 +1583,19 @@ def test_copyfile_character_device(self):
self.assertRaisesRegex(shutil.SpecialFileError, 'is a character device',
shutil.copyfile, src_file, '/dev/null')

@os_helper.skip_unless_symlink
@unittest.skipUnless(os.path.exists('/dev/null'), 'requires /dev/null')
def test_copyfile_symlink_to_character_device(self):
tmp_dir = self.mkdtemp()
src = os.path.join(tmp_dir, 'src')
dst = os.path.join(tmp_dir, 'dst')
os.symlink('/dev/null', src)

shutil.copyfile(src, dst, follow_symlinks=False)

self.assertTrue(os.path.islink(dst))
self.assertEqual(os.readlink(dst), '/dev/null')

def test_copyfile_block_device(self):
block_dev = None
for dev in ['/dev/loop0', '/dev/sda', '/dev/vda', '/dev/disk0']:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix :func:`shutil.copyfile` to copy a symbolic link to a special file when
``follow_symlinks=False`` instead of raising
:exc:`~shutil.SpecialFileError`.
Loading