From 263b7f6fdd2bc14b2c8c02b84f0be2387d7bd790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Borgstr=C3=B6m?= Date: Thu, 27 Jun 2013 13:28:59 +0200 Subject: [PATCH] Switch to our own xattr implementation --- darc/archive.py | 17 +++----- darc/testsuite/archiver.py | 11 ++--- darc/testsuite/xattr.py | 33 ++++++++++++++ darc/xattr.py | 89 ++++++++++++++++++++++++++++++++++++++ docs/index.rst | 1 - setup.py | 7 ++- 6 files changed, 138 insertions(+), 20 deletions(-) create mode 100644 darc/testsuite/xattr.py create mode 100644 darc/xattr.py diff --git a/darc/archive.py b/darc/archive.py index bc007cd83..e889fa369 100644 --- a/darc/archive.py +++ b/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): diff --git a/darc/testsuite/archiver.py b/darc/testsuite/archiver.py index c64ce8572..c761d5116 100644 --- a/darc/testsuite/archiver.py +++ b/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')) diff --git a/darc/testsuite/xattr.py b/darc/testsuite/xattr.py new file mode 100644 index 000000000..99e844b9c --- /dev/null +++ b/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'}) diff --git a/darc/xattr.py b/darc/xattr.py new file mode 100644 index 000000000..ea243021d --- /dev/null +++ b/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 diff --git a/docs/index.rst b/docs/index.rst index a1cb16a03..3e0480a9b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -51,7 +51,6 @@ Requirements ------------ * Python >= 3.2 * msgpack-python -* pyxattr Installation diff --git a/setup.py b/setup.py index 3d918609b..c80066704 100644 --- a/setup.py +++ b/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'] )