Merge pull request #6990 from ThomasWaldmann/more-fine-grained-extended-stat-1.2

xattrs / extended stat: improve exception handling (1.2-maint)
This commit is contained in:
TW 2022-09-05 16:06:10 +02:00 committed by GitHub
commit 1b3cbe2461
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 27 additions and 36 deletions

View file

@ -1157,15 +1157,19 @@ class MetadataCollector:
def stat_ext_attrs(self, st, path, fd=None):
attrs = {}
with backup_io('extended stat'):
flags = 0 if self.noflags else get_flags(path, st, fd=fd)
xattrs = {} if self.noxattrs else xattr.get_all(fd or path, follow_symlinks=False)
if not self.noacls:
if not self.noflags:
with backup_io('extended stat (flags)'):
flags = get_flags(path, st, fd=fd)
if flags:
attrs['bsdflags'] = flags
if not self.noxattrs:
with backup_io('extended stat (xattrs)'):
xattrs = xattr.get_all(fd or path, follow_symlinks=False)
if xattrs:
attrs['xattrs'] = StableDict(xattrs)
if not self.noacls:
with backup_io('extended stat (ACLs)'):
acl_get(path, attrs, st, self.numeric_ids, fd=fd)
if xattrs:
attrs['xattrs'] = StableDict(xattrs)
if flags:
attrs['bsdflags'] = flags
return attrs
def stat_attrs(self, st, path, fd=None):

View file

@ -1393,7 +1393,7 @@ class ArchiverTestCase(ArchiverTestCaseBase):
self.cmd('extract', self.repository_location + '::test')
assert xattr.getxattr(b'input/file', b'security.capability') == capabilities
@pytest.mark.skipif(not xattr.XATTR_FAKEROOT, reason='xattr not supported on this system or on this version of'
@pytest.mark.skipif(not xattr.XATTR_FAKEROOT, reason='xattr not supported on this system or on this version of '
'fakeroot')
def test_extract_xattrs_errors(self):
def patched_setxattr_E2BIG(*args, **kwargs):
@ -3486,7 +3486,7 @@ id: 2 / e29442 3506da 4e1ea7 / 25f62a 5a3d41 - 02
assert 'Attic repository detected.' in output
# derived from test_extract_xattrs_errors()
@pytest.mark.skipif(not xattr.XATTR_FAKEROOT, reason='xattr not supported on this system or on this version of'
@pytest.mark.skipif(not xattr.XATTR_FAKEROOT, reason='xattr not supported on this system or on this version of '
'fakeroot')
def test_do_not_fail_when_percent_is_in_xattr_name(self):
"""https://github.com/borgbackup/borg/issues/6063"""
@ -3502,7 +3502,7 @@ id: 2 / e29442 3506da 4e1ea7 / 25f62a 5a3d41 - 02
self.cmd('extract', self.repository_location + '::test', exit_code=EXIT_WARNING)
# derived from test_extract_xattrs_errors()
@pytest.mark.skipif(not xattr.XATTR_FAKEROOT, reason='xattr not supported on this system or on this version of'
@pytest.mark.skipif(not xattr.XATTR_FAKEROOT, reason='xattr not supported on this system or on this version of '
'fakeroot')
def test_do_not_fail_when_percent_is_in_file_name(self):
"""https://github.com/borgbackup/borg/issues/6063"""

View file

@ -80,21 +80,14 @@ def get_all(path, follow_symlinks=False):
# borg always did it like that...
result[name] = getxattr(path, name, follow_symlinks=follow_symlinks) or None
except OSError as e:
name_str = name.decode()
if isinstance(path, int):
path_str = '<FD %d>' % path
else:
path_str = os.fsdecode(path)
if e.errno == ENOATTR:
# if we get ENOATTR, a race has happened: xattr names were deleted after list.
# we just ignore the now missing ones. if you want consistency, do snapshots.
# note: platform.xattr._check has already made a nice exception e with errno, msg, path/fd
if e.errno in (ENOATTR, ): # errors we just ignore silently
# ENOATTR: a race has happened: xattr names were deleted after list.
pass
elif e.errno == errno.EPERM:
# we were not permitted to read this attribute, still can continue trying to read others
logger.warning('{}: Operation not permitted when reading extended attribute {}'.format(
path_str, name_str))
else:
raise
else: # all others: warn, skip this single xattr name, continue processing other xattrs
# EPERM: we were not permitted to read this attribute
# EINVAL: maybe xattr name is invalid or other issue, #6988
logger.warning('when getting extended attribute %s: %s', name.decode(errors='replace'), str(e))
except OSError as e:
if e.errno in (errno.ENOTSUP, errno.EPERM):
# if xattrs are not supported on the filesystem, we give up.
@ -126,24 +119,18 @@ def set_all(path, xattrs, follow_symlinks=False):
# if we have a None value, it means "empty", so give b'' to setxattr in that case:
setxattr(path, k, v or b'', follow_symlinks=follow_symlinks)
except OSError as e:
# note: platform.xattr._check has already made a nice exception e with errno, msg, path/fd
warning = True
k_str = k.decode()
if isinstance(path, int):
path_str = '<FD %d>' % path
else:
path_str = os.fsdecode(path)
if e.errno == errno.E2BIG:
err_str = 'too big for this filesystem'
elif e.errno == errno.ENOTSUP:
err_str = 'xattrs not supported on this filesystem'
err_str = 'too big for this filesystem (%s)' % str(e)
elif e.errno == errno.ENOSPC:
# ext4 reports ENOSPC when trying to set an xattr with >4kiB while ext4 can only support 4kiB xattrs
# (in this case, this is NOT a "disk full" error, just a ext4 limitation).
err_str = 'no space left on device [xattr len = %d]' % (len(v),)
err_str = 'fs full or xattr too big? [xattr len = %d] (%s)' % (len(v), str(e))
else:
# generic handler
# EACCES: permission denied to set this specific xattr (this may happen related to security.* keys)
# EPERM: operation not permitted
err_str = os.strerror(e.errno)
logger.warning('%s: when setting extended attribute %s: %s', path_str, k_str, err_str)
err_str = str(e)
logger.warning('when setting extended attribute %s: %s', k.decode(errors='replace'), err_str)
return warning