mirror of
https://github.com/borgbackup/borg.git
synced 2025-02-22 14:11:27 +00:00
xattr: use bytes typed path for listxattr, getxattr, setxattr
This commit is contained in:
parent
9deb90db71
commit
c29c3063b0
6 changed files with 41 additions and 34 deletions
|
@ -674,6 +674,7 @@ def restore_attrs(self, path, item, symlink=False, fd=None):
|
|||
Does not access the repository.
|
||||
"""
|
||||
backup_io.op = 'attrs'
|
||||
path_bytes = os.fsencode(path)
|
||||
uid = gid = None
|
||||
if not self.numeric_owner:
|
||||
uid = user2uid(item.user)
|
||||
|
@ -728,7 +729,7 @@ def restore_attrs(self, path, item, symlink=False, fd=None):
|
|||
try:
|
||||
# the key k is a bytes object due to msgpack unpacking it as such.
|
||||
# if we have a None value, it means "empty", so give b'' to setxattr in that case:
|
||||
xattr.setxattr(fd or path, k, v or b'', follow_symlinks=False)
|
||||
xattr.setxattr(fd or path_bytes, k, v or b'', follow_symlinks=False)
|
||||
except OSError as e:
|
||||
k_str = k.decode()
|
||||
if e.errno == errno.E2BIG:
|
||||
|
|
|
@ -28,7 +28,7 @@ def listxattr(path, *, follow_symlinks=True):
|
|||
"""
|
||||
Return xattr names of a file (list of bytes objects).
|
||||
|
||||
*path* can either be a path (str or bytes) or an open file descriptor (int).
|
||||
*path* can either be a path (bytes) or an open file descriptor (int).
|
||||
*follow_symlinks* indicates whether symlinks should be followed
|
||||
and only applies when *path* is not an open file descriptor.
|
||||
"""
|
||||
|
@ -39,7 +39,7 @@ def getxattr(path, name, *, follow_symlinks=True):
|
|||
"""
|
||||
Read xattr and return its value (as bytes).
|
||||
|
||||
*path* can either be a path (str or bytes) or an open file descriptor (int).
|
||||
*path* can either be a path (bytes) or an open file descriptor (int).
|
||||
*name* is the name of the xattr to read (bytes).
|
||||
*follow_symlinks* indicates whether symlinks should be followed
|
||||
and only applies when *path* is not an open file descriptor.
|
||||
|
@ -51,7 +51,7 @@ def setxattr(path, name, value, *, follow_symlinks=True):
|
|||
"""
|
||||
Write xattr on *path*.
|
||||
|
||||
*path* can either be a path (str or bytes) or an open file descriptor (int).
|
||||
*path* can either be a path (bytes) or an open file descriptor (int).
|
||||
*name* is the name of the xattr to read (bytes).
|
||||
*value* is the value to write (bytes).
|
||||
*follow_symlinks* indicates whether symlinks should be followed
|
||||
|
|
|
@ -63,8 +63,7 @@ def _check(rv, path=None, detect_buffer_too_small=False):
|
|||
|
||||
|
||||
def _listxattr_inner(func, path):
|
||||
if isinstance(path, str):
|
||||
path = os.fsencode(path)
|
||||
assert isinstance(path, (bytes, int))
|
||||
size = len(buffer)
|
||||
while True:
|
||||
buf = buffer.get(size)
|
||||
|
@ -77,8 +76,7 @@ def _listxattr_inner(func, path):
|
|||
|
||||
|
||||
def _getxattr_inner(func, path, name):
|
||||
if isinstance(path, str):
|
||||
path = os.fsencode(path)
|
||||
assert isinstance(path, (bytes, int))
|
||||
assert isinstance(name, bytes)
|
||||
size = len(buffer)
|
||||
while True:
|
||||
|
@ -92,8 +90,7 @@ def _getxattr_inner(func, path, name):
|
|||
|
||||
|
||||
def _setxattr_inner(func, path, name, value):
|
||||
if isinstance(path, str):
|
||||
path = os.fsencode(path)
|
||||
assert isinstance(path, (bytes, int))
|
||||
assert isinstance(name, bytes)
|
||||
assert isinstance(value, bytes)
|
||||
_check(func(path, name, value, len(value)), path, detect_buffer_too_small=False)
|
||||
|
|
|
@ -335,13 +335,14 @@ def create_test_files(self):
|
|||
os.symlink('somewhere', os.path.join(self.input_path, 'link1'))
|
||||
self.create_regular_file('fusexattr', size=1)
|
||||
if not xattr.XATTR_FAKEROOT and xattr.is_enabled(self.input_path):
|
||||
fn = os.fsencode(os.path.join(self.input_path, 'fusexattr'))
|
||||
# ironically, due to the way how fakeroot works, comparing FUSE file xattrs to orig file xattrs
|
||||
# will FAIL if fakeroot supports xattrs, thus we only set the xattr if XATTR_FAKEROOT is False.
|
||||
# This is because fakeroot with xattr-support does not propagate xattrs of the underlying file
|
||||
# into "fakeroot space". Because the xattrs exposed by borgfs are these of an underlying file
|
||||
# (from fakeroots point of view) they are invisible to the test process inside the fakeroot.
|
||||
xattr.setxattr(os.path.join(self.input_path, 'fusexattr'), b'user.foo', b'bar')
|
||||
xattr.setxattr(os.path.join(self.input_path, 'fusexattr'), b'user.empty', b'')
|
||||
xattr.setxattr(fn, b'user.foo', b'bar')
|
||||
xattr.setxattr(fn, b'user.empty', b'')
|
||||
# XXX this always fails for me
|
||||
# ubuntu 14.04, on a TMP dir filesystem with user_xattr, using fakeroot
|
||||
# same for newer ubuntu and centos.
|
||||
|
@ -1235,13 +1236,13 @@ def patched_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', b'security.capability', capabilities)
|
||||
xattr.setxattr(b'input/file', b'security.capability', capabilities)
|
||||
self.cmd('init', '--encryption=repokey', 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', b'security.capability') == capabilities
|
||||
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'
|
||||
'fakeroot')
|
||||
|
@ -1256,7 +1257,7 @@ def patched_setxattr_EACCES(*args, **kwargs):
|
|||
raise OSError(errno.EACCES, 'EACCES')
|
||||
|
||||
self.create_regular_file('file')
|
||||
xattr.setxattr('input/file', b'attribute', b'value')
|
||||
xattr.setxattr(b'input/file', b'attribute', b'value')
|
||||
self.cmd('init', self.repository_location, '-e' 'none')
|
||||
self.cmd('create', self.repository_location + '::test', 'input')
|
||||
with changedir('output'):
|
||||
|
@ -2183,7 +2184,7 @@ def has_noatime(some_file):
|
|||
# list/read xattrs
|
||||
try:
|
||||
in_fn = 'input/fusexattr'
|
||||
out_fn = os.path.join(mountpoint, 'input', 'fusexattr')
|
||||
out_fn = os.fsencode(os.path.join(mountpoint, 'input', 'fusexattr'))
|
||||
if not xattr.XATTR_FAKEROOT and xattr.is_enabled(self.input_path):
|
||||
assert sorted(no_selinux(xattr.listxattr(out_fn))) == [b'user.empty', b'user.foo', ]
|
||||
assert xattr.getxattr(out_fn, b'user.foo') == b'bar'
|
||||
|
|
|
@ -27,38 +27,43 @@ def assert_equal_se(self, is_x, want_x):
|
|||
self.assert_equal(is_x, want_x)
|
||||
|
||||
def test(self):
|
||||
self.assert_equal_se(listxattr(self.tmpfile.name), [])
|
||||
self.assert_equal_se(listxattr(self.tmpfile.fileno()), [])
|
||||
self.assert_equal_se(listxattr(self.symlink), [])
|
||||
setxattr(self.tmpfile.name, b'user.foo', b'bar')
|
||||
setxattr(self.tmpfile.fileno(), b'user.bar', b'foo')
|
||||
setxattr(self.tmpfile.name, b'user.empty', b'')
|
||||
self.assert_equal_se(listxattr(self.tmpfile.name), [b'user.foo', b'user.bar', b'user.empty'])
|
||||
self.assert_equal_se(listxattr(self.tmpfile.fileno()), [b'user.foo', b'user.bar', b'user.empty'])
|
||||
self.assert_equal_se(listxattr(self.symlink), [b'user.foo', b'user.bar', b'user.empty'])
|
||||
self.assert_equal_se(listxattr(self.symlink, follow_symlinks=False), [])
|
||||
self.assert_equal(getxattr(self.tmpfile.name, b'user.foo'), b'bar')
|
||||
self.assert_equal(getxattr(self.tmpfile.fileno(), b'user.foo'), b'bar')
|
||||
self.assert_equal(getxattr(self.symlink, b'user.foo'), b'bar')
|
||||
self.assert_equal(getxattr(self.tmpfile.name, b'user.empty'), b'')
|
||||
tmp_fn = os.fsencode(self.tmpfile.name)
|
||||
tmp_lfn = os.fsencode(self.symlink)
|
||||
tmp_fd = self.tmpfile.fileno()
|
||||
self.assert_equal_se(listxattr(tmp_fn), [])
|
||||
self.assert_equal_se(listxattr(tmp_fd), [])
|
||||
self.assert_equal_se(listxattr(tmp_lfn), [])
|
||||
setxattr(tmp_fn, b'user.foo', b'bar')
|
||||
setxattr(tmp_fd, b'user.bar', b'foo')
|
||||
setxattr(tmp_fn, b'user.empty', b'')
|
||||
self.assert_equal_se(listxattr(tmp_fn), [b'user.foo', b'user.bar', b'user.empty'])
|
||||
self.assert_equal_se(listxattr(tmp_fd), [b'user.foo', b'user.bar', b'user.empty'])
|
||||
self.assert_equal_se(listxattr(tmp_lfn), [b'user.foo', b'user.bar', b'user.empty'])
|
||||
self.assert_equal_se(listxattr(tmp_lfn, follow_symlinks=False), [])
|
||||
self.assert_equal(getxattr(tmp_fn, b'user.foo'), b'bar')
|
||||
self.assert_equal(getxattr(tmp_fd, b'user.foo'), b'bar')
|
||||
self.assert_equal(getxattr(tmp_lfn, b'user.foo'), b'bar')
|
||||
self.assert_equal(getxattr(tmp_fn, b'user.empty'), b'')
|
||||
|
||||
def test_listxattr_buffer_growth(self):
|
||||
tmp_fn = os.fsencode(self.tmpfile.name)
|
||||
# make it work even with ext4, which imposes rather low limits
|
||||
buffer.resize(size=64, init=True)
|
||||
# xattr raw key list will be size 9 * (10 + 1), which is > 64
|
||||
keys = [b'user.attr%d' % i for i in range(9)]
|
||||
for key in keys:
|
||||
setxattr(self.tmpfile.name, key, b'x')
|
||||
got_keys = listxattr(self.tmpfile.name)
|
||||
setxattr(tmp_fn, key, b'x')
|
||||
got_keys = listxattr(tmp_fn)
|
||||
self.assert_equal_se(got_keys, keys)
|
||||
self.assert_equal(len(buffer), 128)
|
||||
|
||||
def test_getxattr_buffer_growth(self):
|
||||
tmp_fn = os.fsencode(self.tmpfile.name)
|
||||
# make it work even with ext4, which imposes rather low limits
|
||||
buffer.resize(size=64, init=True)
|
||||
value = b'x' * 126
|
||||
setxattr(self.tmpfile.name, b'user.big', value)
|
||||
got_value = getxattr(self.tmpfile.name, b'user.big')
|
||||
setxattr(tmp_fn, b'user.big', value)
|
||||
got_value = getxattr(tmp_fn, b'user.big')
|
||||
self.assert_equal(value, got_value)
|
||||
self.assert_equal(len(buffer), 128)
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
"""A basic extended attributes (xattr) implementation for Linux, FreeBSD and MacOS X."""
|
||||
|
||||
import errno
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from .platform import listxattr, getxattr, setxattr, ENOATTR
|
||||
|
@ -30,6 +31,8 @@ def get_all(path, follow_symlinks=True):
|
|||
The returned mapping maps xattr names (bytes) to values (bytes or None).
|
||||
None indicates, as a xattr value, an empty value, i.e. a value of length zero.
|
||||
"""
|
||||
if isinstance(path, str):
|
||||
path = os.fsencode(path)
|
||||
try:
|
||||
result = {}
|
||||
names = listxattr(path, follow_symlinks=follow_symlinks)
|
||||
|
|
Loading…
Reference in a new issue