Experimental OS X ACL support (#66)

This commit is contained in:
Jonas Borgström 2014-05-03 15:10:11 +02:00
parent db0381e6f2
commit 47c6141aab
5 changed files with 81 additions and 2 deletions

View File

@ -9,7 +9,7 @@ Version 0.13
(feature release, released on X)
- Reduced memory usage when backing up many small files (#69)
- Experimental Linux and FreeBSD ACL support (#66)
- Experimental Linux, OS X 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
- Added cachedir support. CACHEDIR.TAG compatible cache directories

View File

@ -6,6 +6,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
elif platform == 'Darwin':
from attic.platform_darwin import acl_get, acl_set, API_VERSION
else:
API_VERSION = 1

46
attic/platform_darwin.pyx Normal file
View File

@ -0,0 +1,46 @@
import os
API_VERSION = 1
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(acl_t acl, ssize_t *len_p)
int ACL_TYPE_EXTENDED
def acl_get(path, item, numeric_owner=False):
cdef acl_t acl = NULL
cdef char *text = NULL
try:
acl = acl_get_link_np(<bytes>os.fsencode(path), ACL_TYPE_EXTENDED)
if acl == NULL:
return
text = acl_to_text(acl, NULL)
if text == NULL:
return
item[b'acl_extended'] = text
finally:
acl_free(text)
acl_free(acl)
def acl_set(path, item, numeric_owner=False):
cdef acl_t acl = NULL
try:
try:
acl = acl_from_text(item[b'acl_extended'])
except KeyError:
return
if acl == NULL:
return
if acl_set_link_np(<bytes>os.fsencode(path), ACL_TYPE_EXTENDED, acl):
return
finally:
acl_free(acl)

View File

@ -71,3 +71,30 @@ class PlatformLinuxTestCase(AtticTestCase):
self.assert_equal(self.get_acl(self.tmpdir)[b'acl_access'], ACCESS_ACL)
self.assert_equal(self.get_acl(self.tmpdir)[b'acl_default'], DEFAULT_ACL)
@unittest.skipUnless(sys.platform.startswith('darwin'), 'OS X only test')
@unittest.skipIf(fakeroot_detected(), 'not compatible with fakeroot')
class PlatformDarwinTestCase(AtticTestCase):
def setUp(self):
self.tmpdir = tempfile.mkdtemp()
def tearDown(self):
shutil.rmtree(self.tmpdir)
def get_acl(self, path, numeric_owner=False):
item = {}
acl_get(path, item, numeric_owner=numeric_owner)
return item
def set_acl(self, path, acl, numeric_owner=False):
item = {b'acl_extended': acl}
acl_set(path, item, numeric_owner=numeric_owner)
def test_access_acl(self):
file = tempfile.NamedTemporaryFile()
self.assert_equal(self.get_acl(file.name), {})
self.set_acl(file.name, b'!#acl 1\ngroup:ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000014:staff:9999:allow:read\nuser:FFFFEEEE-DDDD-CCCC-BBBB-AAAA00000000:root:0:allow:read\n', numeric_owner=False)
self.assert_in(b'group:ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000014:staff:20:allow:read', self.get_acl(file.name)[b'acl_extended'])
self.assert_in(b'user:FFFFEEEE-DDDD-CCCC-BBBB-AAAA00000000:root:0:allow:read', self.get_acl(file.name)[b'acl_extended'])

View File

@ -25,6 +25,7 @@ crypto_source = 'attic/crypto.pyx'
chunker_source = 'attic/chunker.pyx'
hashindex_source = 'attic/hashindex.pyx'
platform_linux_source = 'attic/platform_linux.pyx'
platform_darwin_source = 'attic/platform_darwin.pyx'
platform_freebsd_source = 'attic/platform_freebsd.pyx'
try:
@ -39,7 +40,7 @@ try:
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', 'attic/platform_freebsd.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', 'attic/platform_darwin.c'])
super(Sdist, self).make_distribution()
except ImportError:
@ -52,6 +53,7 @@ except ImportError:
hashindex_source = hashindex_source.replace('.pyx', '.c')
platform_linux_source = platform_linux_source.replace('.pyx', '.c')
platform_freebsd_source = platform_freebsd_source.replace('.pyx', '.c')
platform_darwin_source = platform_darwin_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, platform_linux_source, platform_freebsd_source]):
raise ImportError('The GIT version of Attic needs Cython. Install Cython or use a released version')
@ -91,6 +93,8 @@ 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]))
elif platform == 'Darwin':
ext_modules.append(Extension('attic.platform_darwin', [platform_darwin_source]))
setup(
name='Attic',