Switch to our own xattr implementation

This commit is contained in:
Jonas Borgström 2013-06-27 13:28:59 +02:00
parent 00bdfa1a8e
commit 263b7f6fdd
6 changed files with 138 additions and 20 deletions

View file

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

View file

@ -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
darc/testsuite/xattr.py Normal file
View file

@ -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
darc/xattr.py Normal file
View file

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

View file

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

View file

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