diff options
author | Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> | 2023-12-23 10:53:48 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-23 10:53:48 (GMT) |
commit | 80b2bad2c2c4e34ee6bd8e6f1f9360f97aed6551 (patch) | |
tree | 1b7e6af21d25b0cabeb7beff843f86c3923471fd /Lib/shutil.py | |
parent | 0bd134d15f48dc5e284f13f1545548cabf50c792 (diff) | |
download | cpython-80b2bad2c2c4e34ee6bd8e6f1f9360f97aed6551.zip cpython-80b2bad2c2c4e34ee6bd8e6f1f9360f97aed6551.tar.gz cpython-80b2bad2c2c4e34ee6bd8e6f1f9360f97aed6551.tar.bz2 |
[3.11] gh-113188: Fix shutil.copymode() and shutil.copystat() on Windows (GH-113285) (GH-113426)
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.
(cherry picked from commit c7874bb56f862dd96a9a74b809dbea5688fa6c1c)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
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 cae65a3..eecc4be 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -298,11 +298,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) @@ -378,8 +382,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, |