[3.12] gh-113188: Fix shutil.copymode() and shutil.copystat() on Windows (GH-113285)

Previously they worked differenly if dst is a symbolic link:
they modified the permission bits of dst itself rather than the file
it points to if follow_symlinks is true or src is not a symbolic link,
and did nothing if follow_symlinks is false and src is a symbolic link.
This commit is contained in:
Serhiy Storchaka 2023-12-23 12:31:52 +02:00 committed by GitHub
parent 4259acd394
commit c7874bb56f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 42 additions and 22 deletions

View file

@ -302,11 +302,15 @@ def copymode(src, dst, *, follow_symlinks=True):
sys.audit("shutil.copymode", src, dst)
if not follow_symlinks and _islink(src) and os.path.islink(dst):
if hasattr(os, 'lchmod'):
if os.name == 'nt':
stat_func, chmod_func = os.lstat, os.chmod
elif hasattr(os, 'lchmod'):
stat_func, chmod_func = os.lstat, os.lchmod
else:
return
else:
if os.name == 'nt' and os.path.islink(dst):
dst = os.path.realpath(dst, strict=True)
stat_func, chmod_func = _stat, os.chmod
st = stat_func(src)
@ -382,8 +386,16 @@ def copystat(src, dst, *, follow_symlinks=True):
# We must copy extended attributes before the file is (potentially)
# chmod()'ed read-only, otherwise setxattr() will error with -EACCES.
_copyxattr(src, dst, follow_symlinks=follow)
_chmod = lookup("chmod")
if os.name == 'nt':
if follow:
if os.path.islink(dst):
dst = os.path.realpath(dst, strict=True)
else:
def _chmod(*args, **kwargs):
os.chmod(*args)
try:
lookup("chmod")(dst, mode, follow_symlinks=follow)
_chmod(dst, mode, follow_symlinks=follow)
except NotImplementedError:
# if we got a NotImplementedError, it's because
# * follow_symlinks=False,