mirror of
https://github.com/borgbackup/borg.git
synced 2025-02-22 06:01:54 +00:00
Experimental FreeBSD ACL support (nfs4 and posix) (#66)
This commit is contained in:
parent
54e757e717
commit
8c25d02481
6 changed files with 132 additions and 25 deletions
2
CHANGES
2
CHANGES
|
@ -8,7 +8,7 @@ Version 0.13
|
|||
|
||||
(feature release, released on X)
|
||||
|
||||
- Experimental Linux ACL support (#66)
|
||||
- Experimental Linux and FreeBSD ACL support (#66)
|
||||
- Added support for backup and restore of BSDFlags (OSX, FreeBSD) (#56)
|
||||
- Fix bug where xattrs on symlinks were not correctly restored
|
||||
|
||||
|
|
|
@ -361,22 +361,7 @@ def group2gid(group, default=None):
|
|||
return default
|
||||
|
||||
|
||||
def acl_use_local_uid_gid(acl):
|
||||
"""Replace the user/group field with the local uid/gid if possible
|
||||
"""
|
||||
entries = []
|
||||
for entry in acl.decode('ascii').split('\n'):
|
||||
if entry:
|
||||
fields = entry.split(':')
|
||||
if fields[0] == 'user' and fields[1]:
|
||||
fields[1] = user2uid(fields[1], fields[3])
|
||||
elif fields[0] == 'group' and fields[1]:
|
||||
fields[1] = group2gid(fields[1], fields[3])
|
||||
entries.append(':'.join(entry.split(':')[:3]))
|
||||
return ('\n'.join(entries)).encode('ascii')
|
||||
|
||||
|
||||
def acl_use_stored_uid_gid(acl):
|
||||
def posix_acl_use_stored_uid_gid(acl):
|
||||
"""Replace the user/group field with the stored uid/gid
|
||||
"""
|
||||
entries = []
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
if platform == 'Linux':
|
||||
from attic.platform_linux import acl_get, acl_set, API_VERSION
|
||||
elif platform == 'FreeBSD':
|
||||
from attic.platform_freebsd import acl_get, acl_set, API_VERSION
|
||||
else:
|
||||
API_VERSION = 1
|
||||
|
||||
|
|
100
attic/platform_freebsd.pyx
Normal file
100
attic/platform_freebsd.pyx
Normal file
|
@ -0,0 +1,100 @@
|
|||
import os
|
||||
from attic.helpers import posix_acl_use_stored_uid_gid
|
||||
|
||||
API_VERSION = 1
|
||||
|
||||
cdef extern from "errno.h":
|
||||
int errno
|
||||
int EINVAL
|
||||
|
||||
cdef extern from "sys/types.h":
|
||||
int ACL_TYPE_ACCESS
|
||||
int ACL_TYPE_DEFAULT
|
||||
int ACL_TYPE_NFS4
|
||||
|
||||
cdef extern from "sys/acl.h":
|
||||
ctypedef struct _acl_t:
|
||||
pass
|
||||
ctypedef _acl_t *acl_t
|
||||
|
||||
int acl_free(void *obj)
|
||||
acl_t acl_get_link_np(const char *path, int type)
|
||||
acl_t acl_set_link_np(const char *path, int type, acl_t acl)
|
||||
acl_t acl_from_text(const char *buf)
|
||||
char *acl_to_text_np(acl_t acl, ssize_t *len, int flags)
|
||||
int ACL_TEXT_NUMERIC_IDS
|
||||
int ACL_TEXT_APPEND_ID
|
||||
|
||||
cdef extern from "unistd.h":
|
||||
long lpathconf(const char *path, int name)
|
||||
int _PC_ACL_NFS4
|
||||
|
||||
|
||||
cdef _get_acl(p, type, item, attribute, int flags):
|
||||
cdef acl_t acl
|
||||
cdef char *text
|
||||
acl = acl_get_link_np(p, type)
|
||||
if acl:
|
||||
text = acl_to_text_np(acl, NULL, flags)
|
||||
if text:
|
||||
item[attribute] = text
|
||||
acl_free(text)
|
||||
acl_free(acl)
|
||||
|
||||
|
||||
def acl_get(path, item, numeric_owner=False):
|
||||
"""Saves ACL Entries
|
||||
|
||||
If `numeric_owner` is True the user/group field is not preserved only uid/gid
|
||||
"""
|
||||
cdef int flags = ACL_TEXT_APPEND_ID
|
||||
p = os.fsencode(path)
|
||||
ret = lpathconf(p, _PC_ACL_NFS4)
|
||||
if ret < 0 and errno == EINVAL:
|
||||
return
|
||||
flags |= ACL_TEXT_NUMERIC_IDS if numeric_owner else 0
|
||||
if ret > 0:
|
||||
_get_acl(p, ACL_TYPE_NFS4, item, b'acl_nfs4', flags)
|
||||
else:
|
||||
_get_acl(p, ACL_TYPE_ACCESS, item, b'acl_access', flags)
|
||||
_get_acl(p, ACL_TYPE_DEFAULT, item, b'acl_default', flags)
|
||||
|
||||
|
||||
cdef _set_acl(p, type, item, attribute, numeric_owner=False):
|
||||
cdef acl_t acl
|
||||
text = item.get(attribute)
|
||||
if text:
|
||||
if numeric_owner and type == ACL_TYPE_NFS4:
|
||||
text = _nfs4_use_stored_uid_gid(text)
|
||||
elif numeric_owner and type in(ACL_TYPE_ACCESS, ACL_TYPE_DEFAULT):
|
||||
text = posix_acl_use_stored_uid_gid(text)
|
||||
acl = acl_from_text(<bytes>text)
|
||||
if acl:
|
||||
acl_set_link_np(p, type, acl)
|
||||
acl_free(acl)
|
||||
|
||||
|
||||
cdef _nfs4_use_stored_uid_gid(acl):
|
||||
"""Replace the user/group field with the stored uid/gid
|
||||
"""
|
||||
entries = []
|
||||
for entry in acl.decode('ascii').split('\n'):
|
||||
if entry:
|
||||
if entry.startswith('user:') or entry.startswith('group:'):
|
||||
fields = entry.split(':')
|
||||
entries.append(':'.join(fields[0], fields[5], *fields[2:-1]))
|
||||
else:
|
||||
entries.append(entry)
|
||||
return ('\n'.join(entries)).encode('ascii')
|
||||
|
||||
|
||||
def acl_set(path, item, numeric_owner=False):
|
||||
"""Restore ACL Entries
|
||||
|
||||
If `numeric_owner` is True the stored uid/gid is used instead
|
||||
of the user/group names
|
||||
"""
|
||||
p = os.fsencode(path)
|
||||
_set_acl(p, ACL_TYPE_NFS4, item, b'acl_nfs4', numeric_owner)
|
||||
_set_acl(p, ACL_TYPE_ACCESS, item, b'acl_access', numeric_owner)
|
||||
_set_acl(p, ACL_TYPE_DEFAULT, item, b'acl_default', numeric_owner)
|
|
@ -1,6 +1,6 @@
|
|||
import os
|
||||
import re
|
||||
from attic.helpers import acl_use_local_uid_gid, acl_use_stored_uid_gid, user2uid, group2gid
|
||||
from attic.helpers import posix_acl_use_stored_uid_gid, user2uid, group2gid
|
||||
|
||||
API_VERSION = 1
|
||||
|
||||
|
@ -25,7 +25,23 @@ cdef extern from "acl/libacl.h":
|
|||
|
||||
_comment_re = re.compile(' *#.*', re.M)
|
||||
|
||||
def acl_append_numeric_ids(acl):
|
||||
|
||||
cdef acl_use_local_uid_gid(acl):
|
||||
"""Replace the user/group field with the local uid/gid if possible
|
||||
"""
|
||||
entries = []
|
||||
for entry in acl.decode('ascii').split('\n'):
|
||||
if entry:
|
||||
fields = entry.split(':')
|
||||
if fields[0] == 'user' and fields[1]:
|
||||
fields[1] = user2uid(fields[1], fields[3])
|
||||
elif fields[0] == 'group' and fields[1]:
|
||||
fields[1] = group2gid(fields[1], fields[3])
|
||||
entries.append(':'.join(entry.split(':')[:3]))
|
||||
return ('\n'.join(entries)).encode('ascii')
|
||||
|
||||
|
||||
cdef acl_append_numeric_ids(acl):
|
||||
"""Extend the "POSIX 1003.1e draft standard 17" format with an additional uid/gid field
|
||||
"""
|
||||
entries = []
|
||||
|
@ -41,7 +57,7 @@ def acl_append_numeric_ids(acl):
|
|||
return ('\n'.join(entries)).encode('ascii')
|
||||
|
||||
|
||||
def acl_numeric_ids(acl):
|
||||
cdef acl_numeric_ids(acl):
|
||||
"""Replace the "POSIX 1003.1e draft standard 17" user/group field with uid/gid
|
||||
"""
|
||||
entries = []
|
||||
|
@ -100,7 +116,7 @@ def acl_set(path, item, numeric_owner=False):
|
|||
cdef acl_t access_acl = NULL
|
||||
cdef acl_t default_acl = NULL
|
||||
if numeric_owner:
|
||||
converter = acl_use_stored_uid_gid
|
||||
converter = posix_acl_use_stored_uid_gid
|
||||
else:
|
||||
converter = acl_use_local_uid_gid
|
||||
access_text = item.get(b'acl_access')
|
||||
|
|
12
setup.py
12
setup.py
|
@ -25,6 +25,7 @@
|
|||
chunker_source = 'attic/chunker.pyx'
|
||||
hashindex_source = 'attic/hashindex.pyx'
|
||||
platform_linux_source = 'attic/platform_linux.pyx'
|
||||
platform_freebsd_source = 'attic/platform_freebsd.pyx'
|
||||
|
||||
try:
|
||||
from Cython.Distutils import build_ext
|
||||
|
@ -38,7 +39,7 @@ def __init__(self, *args, **kwargs):
|
|||
versioneer.cmd_sdist.__init__(self, *args, **kwargs)
|
||||
|
||||
def make_distribution(self):
|
||||
self.filelist.extend(['attic/crypto.c', 'attic/chunker.c', 'attic/_chunker.c', 'attic/hashindex.c', 'attic/_hashindex.c', 'attic/platform_linux.c'])
|
||||
self.filelist.extend(['attic/crypto.c', 'attic/chunker.c', 'attic/_chunker.c', 'attic/hashindex.c', 'attic/_hashindex.c', 'attic/platform_linux.c', 'attic/platform_freebsd.c'])
|
||||
super(Sdist, self).make_distribution()
|
||||
|
||||
except ImportError:
|
||||
|
@ -49,9 +50,10 @@ def __init__(self, *args, **kwargs):
|
|||
crypto_source = crypto_source.replace('.pyx', '.c')
|
||||
chunker_source = chunker_source.replace('.pyx', '.c')
|
||||
hashindex_source = hashindex_source.replace('.pyx', '.c')
|
||||
acl_source = platform_linux_source.replace('.pyx', '.c')
|
||||
platform_linux_source = platform_linux_source.replace('.pyx', '.c')
|
||||
platform_freebsd_source = platform_freebsd_source.replace('.pyx', '.c')
|
||||
from distutils.command.build_ext import build_ext
|
||||
if not all(os.path.exists(path) for path in [crypto_source, chunker_source, hashindex_source, acl_source]):
|
||||
if not all(os.path.exists(path) for path in [crypto_source, chunker_source, hashindex_source, platform_linux_source, platform_freebsd_source]):
|
||||
raise ImportError('The GIT version of Attic needs Cython. Install Cython or use a released version')
|
||||
|
||||
|
||||
|
@ -87,11 +89,13 @@ def detect_openssl(prefixes):
|
|||
]
|
||||
if platform == 'Linux':
|
||||
ext_modules.append(Extension('attic.platform_linux', [platform_linux_source], libraries=['acl']))
|
||||
elif platform == 'FreeBSD':
|
||||
ext_modules.append(Extension('attic.platform_freebsd', [platform_freebsd_source]))
|
||||
|
||||
setup(
|
||||
name='Attic',
|
||||
version=versioneer.get_version(),
|
||||
author='Jonas Borgström',
|
||||
author='Jonas Borgstrom',
|
||||
author_email='jonas@borgstrom.se',
|
||||
url='https://attic-backup.org/',
|
||||
description='Deduplicated backups',
|
||||
|
|
Loading…
Reference in a new issue