Kaydet (Commit) 79efbb71 authored tarafından Olexa Bilaniuk's avatar Olexa Bilaniuk Kaydeden (comit) Giampaolo Rodola

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.
üst 948ed8c9
...@@ -359,6 +359,9 @@ def copystat(src, dst, *, follow_symlinks=True): ...@@ -359,6 +359,9 @@ def copystat(src, dst, *, follow_symlinks=True):
mode = stat.S_IMODE(st.st_mode) mode = stat.S_IMODE(st.st_mode)
lookup("utime")(dst, ns=(st.st_atime_ns, st.st_mtime_ns), lookup("utime")(dst, ns=(st.st_atime_ns, st.st_mtime_ns),
follow_symlinks=follow) 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: try:
lookup("chmod")(dst, mode, follow_symlinks=follow) lookup("chmod")(dst, mode, follow_symlinks=follow)
except NotImplementedError: except NotImplementedError:
...@@ -382,7 +385,6 @@ def copystat(src, dst, *, follow_symlinks=True): ...@@ -382,7 +385,6 @@ def copystat(src, dst, *, follow_symlinks=True):
break break
else: else:
raise raise
_copyxattr(src, dst, follow_symlinks=follow)
def copy(src, dst, *, follow_symlinks=True): def copy(src, dst, *, follow_symlinks=True):
"""Copy data and mode bits ("cp src dst"). Return the file's destination. """Copy data and mode bits ("cp src dst"). Return the file's destination.
......
...@@ -531,12 +531,20 @@ class TestShutil(unittest.TestCase): ...@@ -531,12 +531,20 @@ class TestShutil(unittest.TestCase):
# test that shutil.copystat copies xattrs # test that shutil.copystat copies xattrs
src = os.path.join(tmp_dir, 'the_original') src = os.path.join(tmp_dir, 'the_original')
srcro = os.path.join(tmp_dir, 'the_original_ro')
write_file(src, src) write_file(src, src)
write_file(srcro, srcro)
os.setxattr(src, 'user.the_value', b'fiddly') 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') dst = os.path.join(tmp_dir, 'the_copy')
dstro = os.path.join(tmp_dir, 'the_copy_ro')
write_file(dst, dst) write_file(dst, dst)
write_file(dstro, dstro)
shutil.copystat(src, dst) shutil.copystat(src, dst)
shutil.copystat(srcro, dstro)
self.assertEqual(os.getxattr(dst, 'user.the_value'), b'fiddly') 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_symlink
@support.skip_unless_xattr @support.skip_unless_xattr
......
...@@ -149,6 +149,7 @@ Stephen Bevan ...@@ -149,6 +149,7 @@ Stephen Bevan
Ron Bickers Ron Bickers
Natalia B. Bidart Natalia B. Bidart
Adrian von Bidder Adrian von Bidder
Olexa Bilaniuk
David Binger David Binger
Dominic Binks Dominic Binks
Philippe Biondi Philippe Biondi
......
In `shutil.copystat()`, first copy extended file attributes and then file
permissions, since extended attributes can only be set on the destination
while it is still writeable.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment