浏览代码

Experimental OS X ACL support (#66)

Jonas Borgström 11 年之前
父节点
当前提交
47c6141aab
共有 5 个文件被更改,包括 81 次插入2 次删除
  1. 1 1
      CHANGES
  2. 2 0
      attic/platform.py
  3. 46 0
      attic/platform_darwin.pyx
  4. 27 0
      attic/testsuite/platform.py
  5. 5 1
      setup.py

+ 1 - 1
CHANGES

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

+ 2 - 0
attic/platform.py

@@ -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 - 0
attic/platform_darwin.pyx

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

+ 27 - 0
attic/testsuite/platform.py

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

+ 5 - 1
setup.py

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