diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2023-12-23 10:31:52 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-23 10:31:52 (GMT) |
commit | c7874bb56f862dd96a9a74b809dbea5688fa6c1c (patch) | |
tree | abf251674d5b8f024f2af0116fdecbdd67ac0c52 /Lib/shutil.py | |
parent | 4259acd39464b292075f75b7604535cb6158c25b (diff) | |
download | cpython-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.py | 16 |
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, |