1
0
Fork 0
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:
Thomas Waldmann 2018-07-05 15:29:30 +02:00
parent 9deb90db71
commit c29c3063b0
6 changed files with 41 additions and 34 deletions

View file

@ -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:

View file

@ -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

View file

@ -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)

View file

@ -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'

View file

@ -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)

View file

@ -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)