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.
This commit is contained in:
parent
948ed8c96b
commit
79efbb7193
@ -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):
|
|||||||
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):
|
|||||||
|
|
||||||
# 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
|
|||||||
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
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
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.
|
Loading…
x
Reference in New Issue
Block a user