From: Rob Browning Date: Sun, 27 Feb 2011 22:15:24 +0000 (-0600) Subject: Don't touch correct target xattrs; remove inappropriate target xattrs. X-Git-Tag: bup-0.25-rc1~68^2~2 X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=bup.git;a=commitdiff_plain;h=d0af12b1a4023f8295840e64b6ee0d5848d576ba Don't touch correct target xattrs; remove inappropriate target xattrs. When applying xattr metadata, make sure to remove any target xattrs that aren't in the metadata record, but don't touch target xattrs that already match the metadata record. Add corresponding tests. Throw an ApplyError() if xattr set() or remove() return EPERM from within _apply_linux_xattr_rec(). Remove lib/bup/t/testfs and lib/bup/t/testfs.img in the clean target. --- diff --git a/Makefile b/Makefile index cbf548c..2069099 100644 --- a/Makefile +++ b/Makefile @@ -145,6 +145,7 @@ clean: Documentation/clean *.pyc */*.pyc lib/*/*.pyc lib/*/*/*.pyc \ bup bup-* cmd/bup-* lib/bup/_version.py randomgen memtest \ out[12] out2[tc] tags[12] tags2[tc] \ - testfs.img + testfs.img lib/bup/t/testfs.img rm -rf *.tmp t/*.tmp lib/*/*/*.tmp build lib/bup/build if test -e testfs; then rmdir testfs; fi + if test -e lib/bup/t/testfs; then rmdir lib/bup/t/testfs; fi diff --git a/lib/bup/metadata.py b/lib/bup/metadata.py index bb16892..a04d01f 100644 --- a/lib/bup/metadata.py +++ b/lib/bup/metadata.py @@ -465,9 +465,27 @@ class Metadata: self.linux_xattr = result def _apply_linux_xattr_rec(self, path, restore_numeric_ids=False): + existing_xattrs = set(xattr.list(path, nofollow=True)) if(self.linux_xattr): for k, v in self.linux_xattr: - xattr.set(path, k, v, nofollow=True) + if k not in existing_xattrs \ + or v != xattr.get(path, k, nofollow=True): + try: + xattr.set(path, k, v, nofollow=True) + except IOError, e: + if e.errno == errno.EPERM: + raise ApplyError('xattr.set: %s' % e) + else: + raise + existing_xattrs -= frozenset([k]) + for k in existing_xattrs: + try: + xattr.remove(path, k, nofollow=True) + except IOError, e: + if e.errno == errno.EPERM: + raise ApplyError('xattr.remove: %s' % e) + else: + raise def __init__(self): # optional members diff --git a/lib/bup/t/tmetadata.py b/lib/bup/t/tmetadata.py index 24e43f7..be583dc 100644 --- a/lib/bup/t/tmetadata.py +++ b/lib/bup/t/tmetadata.py @@ -3,12 +3,50 @@ import pwd import stat import subprocess import tempfile +import xattr import bup.helpers as helpers from bup import metadata from bup.helpers import clear_errors, detect_fakeroot from wvtest import * +top_dir = os.getcwd() + + +def ex(*cmd): + try: + cmd_str = ' '.join(cmd) + print >> sys.stderr, cmd_str + rc = subprocess.call(cmd) + if rc < 0: + print >> sys.stderr, 'terminated by signal', - rc + sys.exit(1) + elif rc > 0: + print >> sys.stderr, 'returned exit status', rc + sys.exit(1) + except OSError, e: + print >> sys.stderr, 'subprocess call failed:', e + sys.exit(1) + + +def setup_testfs(): + # Set up testfs with user_xattr, etc. + subprocess.call(['umount', 'testfs']) + ex('dd', 'if=/dev/zero', 'of=testfs.img', 'bs=1M', 'count=32') + ex('mke2fs', '-F', '-j', '-m', '0', 'testfs.img') + ex('rm', '-rf', 'testfs') + os.mkdir('testfs') + ex('mount', '-o', 'loop,acl,user_xattr', 'testfs.img', 'testfs') + # Hide, so that tests can't create risks. + ex('chown', 'root:root', 'testfs') + os.chmod('testfs', 0700) + + +def cleanup_testfs(): + subprocess.call(['umount', 'testfs']) + subprocess.call(['rm', '-f', 'testfs.img']) + + @wvtest def test_clean_up_archive_path(): cleanup = metadata._clean_up_path_for_archive @@ -194,3 +232,29 @@ def test_restore_over_existing_target(): WVEXCEPT(Exception, dir_m.create_path, path, create_symlinks=True) finally: subprocess.call(['rm', '-rf', tmpdir]) + + +@wvtest +def test_handling_of_incorrect_existing_linux_xattrs(): + if os.geteuid() != 0 or detect_fakeroot(): + return + setup_testfs() + subprocess.check_call('rm -rf testfs/*', shell=True) + path = 'testfs/foo' + open(path, 'w').close() + xattr.set(path, 'foo', 'bar', namespace=xattr.NS_USER) + m = metadata.from_path(path, archive_path=path, save_symlinks=True) + xattr.set(path, 'baz', 'bax', namespace=xattr.NS_USER) + m.apply_to_path(path, restore_numeric_ids=False) + WVPASSEQ(xattr.list(path), ['user.foo']) + WVPASSEQ(xattr.get(path, 'user.foo'), 'bar') + xattr.set(path, 'foo', 'baz', namespace=xattr.NS_USER) + m.apply_to_path(path, restore_numeric_ids=False) + WVPASSEQ(xattr.list(path), ['user.foo']) + WVPASSEQ(xattr.get(path, 'user.foo'), 'bar') + xattr.remove(path, 'foo', namespace=xattr.NS_USER) + m.apply_to_path(path, restore_numeric_ids=False) + WVPASSEQ(xattr.list(path), ['user.foo']) + WVPASSEQ(xattr.get(path, 'user.foo'), 'bar') + os.chdir(top_dir) + cleanup_testfs()