Browse Source

Switch to our own xattr implementation

Jonas Borgström 12 năm trước cách đây
mục cha
commit
263b7f6fdd
6 tập tin đã thay đổi với 138 bổ sung20 xóa
  1. 5 12
      darc/archive.py
  2. 6 5
      darc/testsuite/archiver.py
  3. 33 0
      darc/testsuite/xattr.py
  4. 89 0
      darc/xattr.py
  5. 0 1
      docs/index.rst
  6. 5 2
      setup.py

+ 5 - 12
darc/archive.py

@@ -8,8 +8,7 @@ import stat
 import sys
 import time
 from io import BytesIO
-import xattr
-
+from . import xattr
 from .chunker import chunkify
 from .helpers import uid2user, user2uid, gid2group, group2gid, \
     Statistics, decode_dict, st_mtime_ns
@@ -280,10 +279,7 @@ class Archive(object):
         xattrs = item.get(b'xattrs')
         if xattrs:
             for k, v in xattrs.items():
-                try:
-                    xattr.set(fd or path, k, v)
-                except (EnvironmentError):
-                    pass
+                xattr.set(fd or path, k, v)
         uid = gid = None
         if not self.numeric_owner:
             uid = user2uid(item[b'user'])
@@ -352,12 +348,9 @@ class Archive(object):
         }
         if self.numeric_owner:
             item[b'user'] = item[b'group'] = None
-        try:
-            xattrs = xattr.get_all(path, True)
-            if xattrs:
-                item[b'xattrs'] = dict(xattrs)
-        except EnvironmentError:
-            pass
+        xattrs = xattr.get_all(path)
+        if xattrs:
+            item[b'xattrs'] = xattrs
         return item
 
     def process_item(self, path, st):

+ 6 - 5
darc/testsuite/archiver.py

@@ -5,8 +5,7 @@ import stat
 import sys
 import shutil
 import tempfile
-import xattr
-
+from darc import xattr
 from darc.archiver import Archiver
 from darc.repository import Repository
 from darc.testsuite import DarcTestCase
@@ -23,7 +22,7 @@ class ArchiverTestCase(DarcTestCase):
 
     def setUp(self):
         self.archiver = Archiver()
-        self.tmpdir = tempfile.mkdtemp()
+        self.tmpdir = tempfile.mkdtemp(dir=os.getcwd())
         self.repository_path = os.path.join(self.tmpdir, 'repository')
         self.repository_location = self.prefix + self.repository_path
         self.input_path = os.path.join(self.tmpdir, 'input')
@@ -36,10 +35,12 @@ class ArchiverTestCase(DarcTestCase):
         os.mkdir(self.output_path)
         os.mkdir(self.keys_path)
         os.mkdir(self.cache_path)
+        self._old_wd = os.getcwd()
         os.chdir(self.tmpdir)
 
     def tearDown(self):
         shutil.rmtree(self.tmpdir)
+        os.chdir(self._old_wd)
 
     def darc(self, *args, **kwargs):
         exit_code = kwargs.get('exit_code', 0)
@@ -70,7 +71,7 @@ class ArchiverTestCase(DarcTestCase):
 
     def get_xattrs(self, path):
         try:
-            return xattr.get_all(path, True)
+            return xattr.get_all(path)
         except EnvironmentError:
             return {}
 
@@ -111,7 +112,7 @@ class ArchiverTestCase(DarcTestCase):
         os.mknod('input/bdev', 0o600 | stat.S_IFBLK,  os.makedev(10, 20))
         # Char device
         os.mknod('input/cdev', 0o600 | stat.S_IFCHR,  os.makedev(30, 40))
-        xattr.set(os.path.join(self.input_path, 'file1'), 'user.foo', 'bar')
+        xattr.set(os.path.join(self.input_path, 'file1'), b'foo', b'bar')
         # Hard link
         os.link(os.path.join(self.input_path, 'file1'),
                 os.path.join(self.input_path, 'hardlink'))

+ 33 - 0
darc/testsuite/xattr.py

@@ -0,0 +1,33 @@
+import tempfile
+import os
+from darc.testsuite import DarcTestCase
+from darc.xattr import lsetxattr, llistxattr, lgetxattr, get_all, set, flistxattr, fgetxattr, fsetxattr
+
+
+class XattrTestCase(DarcTestCase):
+
+    def test_low_level(self):
+        with tempfile.NamedTemporaryFile(dir=os.getcwd()) as fd:
+            self.assert_equal(llistxattr(fd.name), [])
+            lsetxattr(fd.name, b'user.foo', b'bar')
+            self.assert_equal(llistxattr(fd.name), [b'user.foo'])
+            self.assert_equal(lgetxattr(fd.name, b'user.foo'), b'bar')
+
+    def test_low_level_fileno(self):
+        with tempfile.NamedTemporaryFile(dir=os.getcwd()) as fd:
+            self.assert_equal(flistxattr(fd.fileno()), [])
+            fsetxattr(fd.fileno(), b'user.foo', b'bar')
+            self.assert_equal(flistxattr(fd.fileno()), [b'user.foo'])
+            self.assert_equal(fgetxattr(fd.fileno(), b'user.foo'), b'bar')
+
+    def test_high_level(self):
+        with tempfile.NamedTemporaryFile(dir=os.getcwd()) as fd:
+            self.assert_equal(get_all(fd.name), {})
+            set(fd.name, b'foo', b'bar')
+            self.assert_equal(get_all(fd.name), {b'foo': b'bar'})
+
+    def test_high_level_fileno(self):
+        with tempfile.NamedTemporaryFile(dir=os.getcwd()) as fd:
+            self.assert_equal(get_all(fd.fileno()), {})
+            set(fd.fileno(), b'foo', b'bar')
+            self.assert_equal(get_all(fd.fileno()), {b'foo': b'bar'})

+ 89 - 0
darc/xattr.py

@@ -0,0 +1,89 @@
+"""A basic extended attributes (xattr) implementation for Linux
+"""
+import os
+from ctypes import CDLL, create_string_buffer, c_size_t, c_char_p, c_int, get_errno
+from ctypes.util import find_library
+
+libc = CDLL(find_library('c'), use_errno=True)
+libc.llistxattr.argtypes = (c_char_p, c_char_p, c_size_t)
+libc.llistxattr.restype = c_size_t
+libc.flistxattr.argtypes = (c_int, c_char_p, c_size_t)
+libc.flistxattr.restype = c_size_t
+libc.lsetxattr.argtypes = (c_char_p, c_char_p, c_char_p, c_size_t, c_int)
+libc.lsetxattr.restype = c_int
+libc.lgetxattr.argtypes = (c_char_p, c_char_p, c_char_p, c_size_t)
+libc.lgetxattr.restype = c_size_t
+
+
+def set(path_or_fd, name, value):
+    if isinstance(path_or_fd, int):
+        fsetxattr(path_or_fd, b'user.' + name, value)
+    else:
+        lsetxattr(path_or_fd, b'user.' + name, value)
+
+
+def get_all(path_or_fd):
+    """Return a dictionary with all (user) xattrs for "path_or_fd"
+    """
+    if isinstance(path_or_fd, int):
+        return dict((name[5:], fgetxattr(path_or_fd, name)) for name in flistxattr(path_or_fd) if name.startswith(b'user.'))
+    else:
+        return dict((name[5:], lgetxattr(path_or_fd, name)) for name in llistxattr(path_or_fd) if name.startswith(b'user.'))
+
+
+def llistxattr(path):
+    path = os.fsencode(path)
+    n = libc.llistxattr(path, None, 0)
+    if n == 0:
+        []
+    elif n < 0:
+        raise OSError(get_errno())
+    namebuf = create_string_buffer(n)
+    assert libc.llistxattr(path, namebuf, n) == n
+    return namebuf.raw.split(b'\0')[:-1]
+
+
+def flistxattr(fd):
+    n = libc.flistxattr(fd, None, 0)
+    if n == 0:
+        []
+    elif n < 0:
+        raise OSError(get_errno())
+    namebuf = create_string_buffer(n)
+    assert libc.flistxattr(fd, namebuf, n) == n
+    return namebuf.raw.split(b'\0')[:-1]
+
+
+def lsetxattr(path, name, value, flags=0):
+    rv = libc.lsetxattr(os.fsencode(path), name, value, len(value), flags)
+    if rv:
+        raise OSError(get_errno())
+
+
+def fsetxattr(fd, name, value, flags=0):
+    rv = libc.fsetxattr(fd, name, value, len(value), flags)
+    if rv:
+        raise OSError(get_errno())
+
+
+def lgetxattr(path, name):
+    path = os.fsencode(path)
+    n = libc.lgetxattr(path, name, None, 0)
+    if n == 0:
+        return None
+    elif n < 0:
+        raise OSError(get_errno())
+    valuebuf = create_string_buffer(n)
+    assert libc.lgetxattr(path, name, valuebuf, n) == n
+    return valuebuf.raw
+
+
+def fgetxattr(fd, name):
+    n = libc.fgetxattr(fd, name, None, 0)
+    if n == 0:
+        return None
+    elif n < 0:
+        raise OSError(get_errno())
+    valuebuf = create_string_buffer(n)
+    assert libc.fgetxattr(fd, name, valuebuf, n) == n
+    return valuebuf.raw

+ 0 - 1
docs/index.rst

@@ -51,7 +51,6 @@ Requirements
 ------------
 * Python >= 3.2
 * msgpack-python
-* pyxattr
 
 
 Installation

+ 5 - 2
setup.py

@@ -12,7 +12,10 @@ if sys.version_info < min_python:
 
 #from distutils.core import setup
 #from distutils.extension import Extension
-from setuptools import setup, Extension
+try:
+    from setuptools import setup, Extension
+except ImportError:
+    from distutils.core import setup
 from distutils.command.sdist import sdist
 
 chunker_source = 'darc/chunker.pyx'
@@ -69,5 +72,5 @@ setup(
         Extension('darc.chunker', [chunker_source]),
         Extension('darc.hashindex', [hashindex_source])
     ],
-    install_requires=['msgpack-python', 'pyxattr']
+    install_requires=['msgpack-python']
 )