mirror of
https://github.com/borgbackup/borg.git
synced 2026-02-20 00:10:35 -05:00
Fix capabilities extraction on Linux
They are extracted correctly, for a little while at least, since chown() *resets* all capabilities on the chowned file. Which I find curious, since chown() is a privileged syscall. Probably a safeguard for sysadmins who are unaware of capabilities. The solution is to set the xattrs last, after chown()ing files.
This commit is contained in:
parent
8662202486
commit
39a40cd7b7
3 changed files with 52 additions and 11 deletions
|
|
@ -372,17 +372,6 @@ Number of files: {0.stats.nfiles}'''.format(
|
|||
raise Exception('Unknown archive item type %r' % item[b'mode'])
|
||||
|
||||
def restore_attrs(self, path, item, symlink=False, fd=None):
|
||||
xattrs = item.get(b'xattrs', {})
|
||||
for k, v in xattrs.items():
|
||||
try:
|
||||
xattr.setxattr(fd or path, k, v, follow_symlinks=False)
|
||||
except OSError as e:
|
||||
if e.errno not in (errno.ENOTSUP, errno.EACCES, ):
|
||||
# only raise if the errno is not on our ignore list:
|
||||
# ENOTSUP == xattrs not supported here
|
||||
# EACCES == permission denied to set this specific xattr
|
||||
# (this may happen related to security.* keys)
|
||||
raise
|
||||
uid = gid = None
|
||||
if not self.numeric_owner:
|
||||
uid = user2uid(item[b'user'])
|
||||
|
|
@ -420,6 +409,19 @@ Number of files: {0.stats.nfiles}'''.format(
|
|||
os.lchflags(path, item[b'bsdflags'])
|
||||
except OSError:
|
||||
pass
|
||||
# chown removes Linux capabilities, so set the extended attributes at the end, after chown, since they include
|
||||
# the Linux capabilities in the "security.capability" attribute.
|
||||
xattrs = item.get(b'xattrs', {})
|
||||
for k, v in xattrs.items():
|
||||
try:
|
||||
xattr.setxattr(fd or path, k, v, follow_symlinks=False)
|
||||
except OSError as e:
|
||||
if e.errno not in (errno.ENOTSUP, errno.EACCES):
|
||||
# only raise if the errno is not on our ignore list:
|
||||
# ENOTSUP == xattrs not supported here
|
||||
# EACCES == permission denied to set this specific xattr
|
||||
# (this may happen related to security.* keys)
|
||||
raise
|
||||
|
||||
def rename(self, name):
|
||||
if name in self.manifest.archives:
|
||||
|
|
|
|||
|
|
@ -639,6 +639,27 @@ class ArchiverTestCase(ArchiverTestCaseBase):
|
|||
self.assert_equal(sorted(os.listdir('output/input/taggedall')),
|
||||
['.NOBACKUP1', '.NOBACKUP2', 'CACHEDIR.TAG', ])
|
||||
|
||||
@pytest.mark.skipif(not xattr.XATTR_FAKEROOT, reason='Linux capabilities test, requires fakeroot >= 1.20.2')
|
||||
def test_extract_capabilities(self):
|
||||
fchown = os.fchown
|
||||
|
||||
# We need to manually patch chown to get the behaviour Linux has, since fakeroot does not
|
||||
# accurately model the interaction of chown(2) and Linux capabilities, i.e. it does not remove them.
|
||||
def patched_fchown(fd, uid, gid):
|
||||
xattr.setxattr(fd, 'security.capability', None, follow_symlinks=False)
|
||||
fchown(fd, uid, gid)
|
||||
|
||||
# The capability descriptor used here is valid and taken from a /usr/bin/ping
|
||||
capabilities = b'\x01\x00\x00\x02\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
self.create_regular_file('file')
|
||||
xattr.setxattr('input/file', 'security.capability', capabilities)
|
||||
self.cmd('init', self.repository_location)
|
||||
self.cmd('create', self.repository_location + '::test', 'input')
|
||||
with changedir('output'):
|
||||
with patch.object(os, 'fchown', patched_fchown):
|
||||
self.cmd('extract', self.repository_location + '::test')
|
||||
assert xattr.getxattr('input/file', 'security.capability') == capabilities
|
||||
|
||||
def test_path_normalization(self):
|
||||
self.cmd('init', self.repository_location)
|
||||
self.create_regular_file('dir1/dir2/file', size=1024 * 80)
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@
|
|||
"""
|
||||
import errno
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from ctypes import CDLL, create_string_buffer, c_ssize_t, c_size_t, c_char_p, c_int, c_uint32, get_errno
|
||||
from ctypes.util import find_library
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
from .logger import create_logger
|
||||
logger = create_logger()
|
||||
|
|
@ -46,6 +48,22 @@ if libc_name is None:
|
|||
logger.error(msg)
|
||||
raise Exception(msg)
|
||||
|
||||
# If we are running with fakeroot on Linux, then use the xattr functions of fakeroot. This is needed by
|
||||
# the 'test_extract_capabilities' test, but also allows xattrs to work with fakeroot on Linux in normal use.
|
||||
# TODO: Check whether fakeroot supports xattrs on all platforms supported below.
|
||||
# TODO: If that's the case then we can make Borg fakeroot-xattr-compatible on these as well.
|
||||
LD_PRELOAD = os.environ.get('LD_PRELOAD', '')
|
||||
XATTR_FAKEROOT = False
|
||||
if sys.platform.startswith('linux') and 'fakeroot' in LD_PRELOAD:
|
||||
fakeroot_version = LooseVersion(subprocess.check_output(['fakeroot', '-v']).decode('ascii').split()[-1])
|
||||
if fakeroot_version >= LooseVersion("1.20.2"):
|
||||
# 1.20.2 has been confirmed to have xattr support
|
||||
# 1.18.2 has been confirmed not to have xattr support
|
||||
# Versions in-between are unknown
|
||||
libc_name = LD_PRELOAD
|
||||
XATTR_FAKEROOT = True
|
||||
|
||||
|
||||
try:
|
||||
libc = CDLL(libc_name, use_errno=True)
|
||||
except OSError as e:
|
||||
|
|
|
|||
Loading…
Reference in a new issue