Jelajahi Sumber

xattr: use bytes typed path for listxattr, getxattr, setxattr

Thomas Waldmann 7 tahun lalu
induk
melakukan
c29c3063b0

+ 2 - 1
src/borg/archive.py

@@ -674,6 +674,7 @@ Utilization of max. archive size: {csize_max:.0%}
         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 @@ Utilization of max. archive size: {csize_max:.0%}
             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:

+ 3 - 3
src/borg/platform/base.py

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

+ 3 - 6
src/borg/platform/xattr.py

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

+ 7 - 6
src/borg/testsuite/archiver.py

@@ -335,13 +335,14 @@ class ArchiverTestCaseBase(BaseTestCase):
             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 @@ class ArchiverTestCase(ArchiverTestCaseBase):
         # 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 @@ class ArchiverTestCase(ArchiverTestCaseBase):
             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 @@ class ArchiverTestCase(ArchiverTestCaseBase):
             # 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'

+ 23 - 18
src/borg/testsuite/xattr.py

@@ -27,38 +27,43 @@ class XattrTestCase(BaseTestCase):
         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)
 

+ 3 - 0
src/borg/xattr.py

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