mirror of
https://github.com/borgbackup/borg.git
synced 2026-06-09 00:32:37 -04:00
Switch to our own xattr implementation
This commit is contained in:
parent
00bdfa1a8e
commit
263b7f6fdd
6 changed files with 138 additions and 20 deletions
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
33
darc/testsuite/xattr.py
Normal 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
89
darc/xattr.py
Normal 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
|
||||
|
|
@ -51,7 +51,6 @@ Requirements
|
|||
------------
|
||||
* Python >= 3.2
|
||||
* msgpack-python
|
||||
* pyxattr
|
||||
|
||||
|
||||
Installation
|
||||
|
|
|
|||
7
setup.py
7
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']
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue