Browse Source

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 years ago
parent
commit
98198b34d9
2 changed files with 13 additions and 4 deletions
  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):
         # 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 = ['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:
             setxattr(self.tmpfile.name, key, b'x')
         got_keys = listxattr(self.tmpfile.name)
         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):
         # 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.restype = c_int
     ns = EXTATTR_NAMESPACE_USER = 0x0001
+    prefix = 'user.'
 
     def listxattr(path, *, follow_symlinks=True):
         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)
 
         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 func(path, name, buf, size):
@@ -347,6 +348,10 @@ elif sys.platform.startswith('freebsd'):  # pragma: freebsd only
                 else:
                     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)
         return buf[:n] or None
 
@@ -360,6 +365,10 @@ elif sys.platform.startswith('freebsd'):  # pragma: freebsd only
                 else:
                     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)
 
 else:  # pragma: unknown platform only