diff options
author | Olexa Bilaniuk <obilaniu@users.noreply.github.com> | 2019-05-10 03:22:06 (GMT) |
---|---|---|
committer | Giampaolo Rodola <g.rodola@gmail.com> | 2019-05-10 03:22:06 (GMT) |
commit | 79efbb719383386051c72f2ee932eeca8e033e6b (patch) | |
tree | ca694616676aaad7de81f41ee5d1b16dd18455be /Lib | |
parent | 948ed8c96b6912541a608591efe3e00fb520429a (diff) | |
download | cpython-79efbb719383386051c72f2ee932eeca8e033e6b.zip cpython-79efbb719383386051c72f2ee932eeca8e033e6b.tar.gz cpython-79efbb719383386051c72f2ee932eeca8e033e6b.tar.bz2 |
bpo-24538: Fix bug in shutil involving the copying of xattrs to read-only files. (PR-13212)
Extended attributes can only be set on user-writeable files, but shutil previously
first chmod()ed the destination file to the source's permissions and then tried to
copy xattrs. This will cause failures if attempting to copy read-only files with
xattrs, as occurs with Git clones on Lustre FS.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/shutil.py | 4 | ||||
-rw-r--r-- | Lib/test/test_shutil.py | 8 |
2 files changed, 11 insertions, 1 deletions
diff --git a/Lib/shutil.py b/Lib/shutil.py index 6cfe373..b2e8f5f 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -359,6 +359,9 @@ def copystat(src, dst, *, follow_symlinks=True): mode = stat.S_IMODE(st.st_mode) lookup("utime")(dst, ns=(st.st_atime_ns, st.st_mtime_ns), follow_symlinks=follow) + # 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) try: lookup("chmod")(dst, mode, follow_symlinks=follow) except NotImplementedError: @@ -382,7 +385,6 @@ def copystat(src, dst, *, follow_symlinks=True): break else: raise - _copyxattr(src, dst, follow_symlinks=follow) def copy(src, dst, *, follow_symlinks=True): """Copy data and mode bits ("cp src dst"). Return the file's destination. diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index e709a56..eeebb97 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -531,12 +531,20 @@ class TestShutil(unittest.TestCase): # test that shutil.copystat copies xattrs src = os.path.join(tmp_dir, 'the_original') + srcro = os.path.join(tmp_dir, 'the_original_ro') write_file(src, src) + write_file(srcro, srcro) os.setxattr(src, 'user.the_value', b'fiddly') + os.setxattr(srcro, 'user.the_value', b'fiddly') + os.chmod(srcro, 0o444) dst = os.path.join(tmp_dir, 'the_copy') + dstro = os.path.join(tmp_dir, 'the_copy_ro') write_file(dst, dst) + write_file(dstro, dstro) shutil.copystat(src, dst) + shutil.copystat(srcro, dstro) self.assertEqual(os.getxattr(dst, 'user.the_value'), b'fiddly') + self.assertEqual(os.getxattr(dstro, 'user.the_value'), b'fiddly') @support.skip_unless_symlink @support.skip_unless_xattr |