Bladeren bron

make freebsd xattr platform code api compatible with linux, fixes #3952

i.e. prefix the keys with the namespace, so it is ns.key like on
linux.

still only dealing with the "user" namespace, like before.
in the "system" namespaces there are ACLs (we deal with them via the acl
api, so no problem) and stuff from pnfsd (not sure what exactly).

this change is needed because FreeBSD's FUSE code expects the xattr
keys to be in that format.

it is also needed for cross-platform data exchange, so e.g. if one wants to:
- create archive on linux, extract on freebsd - with "user.xxx" xattrs.
- or vice versa.

archives made with older borg versions on freebsd will still extract correctly
on freebsd (not on linux though) even though they do not have the
namespace prefixes in the archived metadata (it will be interpreted in
same way as if they were prefixed by "user." as we do not support any
other namespace anyway).

(cherry picked from commit 068623748428b80e92c247a7bd692e5a2261c94e)
Thomas Waldmann 6 jaren geleden
bovenliggende
commit
98198b34d9
2 gewijzigde bestanden met toevoegingen van 13 en 4 verwijderingen
  1. 3 3
      src/borg/testsuite/xattr.py
  2. 10 1
      src/borg/xattr.py

+ 3 - 3
src/borg/testsuite/xattr.py

@@ -44,13 +44,13 @@ class XattrTestCase(BaseTestCase):
     def test_listxattr_buffer_growth(self):
     def test_listxattr_buffer_growth(self):
         # make it work even with ext4, which imposes rather low limits
         # make it work even with ext4, which imposes rather low limits
         buffer.resize(size=64, init=True)
         buffer.resize(size=64, init=True)
-        # xattr raw key list will be size 9 * (10 + 1), which is > 64
-        keys = ['user.attr%d' % i for i in range(9)]
+        # xattr raw key list will be > 64
+        keys = ['user.attr%d' % i for i in range(20)]
         for key in keys:
         for key in keys:
             setxattr(self.tmpfile.name, key, b'x')
             setxattr(self.tmpfile.name, key, b'x')
         got_keys = listxattr(self.tmpfile.name)
         got_keys = listxattr(self.tmpfile.name)
         self.assert_equal_se(got_keys, keys)
         self.assert_equal_se(got_keys, keys)
-        self.assert_equal(len(buffer), 128)
+        self.assert_true(len(buffer) > 64)
 
 
     def test_getxattr_buffer_growth(self):
     def test_getxattr_buffer_growth(self):
         # make it work even with ext4, which imposes rather low limits
         # make it work even with ext4, which imposes rather low limits

+ 10 - 1
src/borg/xattr.py

@@ -323,6 +323,7 @@ elif sys.platform.startswith('freebsd'):  # pragma: freebsd only
     libc.extattr_set_file.argtypes = (c_char_p, c_int, c_char_p, c_char_p, c_size_t)
     libc.extattr_set_file.argtypes = (c_char_p, c_int, c_char_p, c_char_p, c_size_t)
     libc.extattr_set_file.restype = c_int
     libc.extattr_set_file.restype = c_int
     ns = EXTATTR_NAMESPACE_USER = 0x0001
     ns = EXTATTR_NAMESPACE_USER = 0x0001
+    prefix = 'user.'
 
 
     def listxattr(path, *, follow_symlinks=True):
     def listxattr(path, *, follow_symlinks=True):
         def func(path, buf, size):
         def func(path, buf, size):
@@ -335,7 +336,7 @@ elif sys.platform.startswith('freebsd'):  # pragma: freebsd only
                     return libc.extattr_list_link(path, ns, buf, size)
                     return libc.extattr_list_link(path, ns, buf, size)
 
 
         n, buf = _listxattr_inner(func, path)
         n, buf = _listxattr_inner(func, path)
-        return [os.fsdecode(name) for name in split_lstring(buf[:n]) if name]
+        return [prefix + os.fsdecode(name) for name in split_lstring(buf[:n]) if name]
 
 
     def getxattr(path, name, *, follow_symlinks=True):
     def getxattr(path, name, *, follow_symlinks=True):
         def func(path, name, buf, size):
         def func(path, name, buf, size):
@@ -347,6 +348,10 @@ elif sys.platform.startswith('freebsd'):  # pragma: freebsd only
                 else:
                 else:
                     return libc.extattr_get_link(path, ns, name, buf, size)
                     return libc.extattr_get_link(path, ns, name, buf, size)
 
 
+        # strip namespace if there, but ignore if not there.
+        # older borg / attic versions did not prefix the namespace to the names.
+        if name.startswith(prefix):
+            name = name[len(prefix):]
         n, buf = _getxattr_inner(func, path, name)
         n, buf = _getxattr_inner(func, path, name)
         return buf[:n] or None
         return buf[:n] or None
 
 
@@ -360,6 +365,10 @@ elif sys.platform.startswith('freebsd'):  # pragma: freebsd only
                 else:
                 else:
                     return libc.extattr_set_link(path, ns, name, value, size)
                     return libc.extattr_set_link(path, ns, name, value, size)
 
 
+        # strip namespace if there, but ignore if not there.
+        # older borg / attic versions did not prefix the namespace to the names.
+        if name.startswith(prefix):
+            name = name[len(prefix):]
         _setxattr_inner(func, path, name, value)
         _setxattr_inner(func, path, name, value)
 
 
 else:  # pragma: unknown platform only
 else:  # pragma: unknown platform only