summaryrefslogtreecommitdiffstats
path: root/Lib/shutil.py
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2023-12-23 10:31:52 (GMT)
committerGitHub <noreply@github.com>2023-12-23 10:31:52 (GMT)
commitc7874bb56f862dd96a9a74b809dbea5688fa6c1c (patch)
treeabf251674d5b8f024f2af0116fdecbdd67ac0c52 /Lib/shutil.py
parent4259acd39464b292075f75b7604535cb6158c25b (diff)
downloadcpython-c7874bb56f862dd96a9a74b809dbea5688fa6c1c.zip
cpython-c7874bb56f862dd96a9a74b809dbea5688fa6c1c.tar.gz
cpython-c7874bb56f862dd96a9a74b809dbea5688fa6c1c.tar.bz2
[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.
Diffstat (limited to 'Lib/shutil.py')
-rw-r--r--Lib/shutil.py16
1 files changed, 14 insertions, 2 deletions
diff --git a/Lib/shutil.py b/Lib/shutil.py
index b99614f..e026088 100644
--- a/Lib/shutil.py
+++ b/Lib/shutil.py
@@ -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,