From c7e280909350b170aff1316ceefa6d8823034fc8 Mon Sep 17 00:00:00 2001 From: Umer Saleem Date: Thu, 20 Oct 2022 22:09:14 +0500 Subject: [PATCH] Add ACL wrapper libraries This commit adds common ACL libraries, libzfsacl for Linux and FreeBSD to provide helper functions to access ACLs. On Linux, libsunacl provides acl() and facl() to be consumed by vfs_zfsacl.c in Samba. libpyzfsacl.c provides python bindings for libzfsacl. A new package for libzfsacl that includes all the libraries is added. Signed-off-by: Umer Saleem --- config/deb.am | 5 +- contrib/debian/control | 12 + .../debian/openzfs-python3-libzfsacl.install | 4 + contrib/debian/openzfs-python3-pyzfs.install | 3 +- contrib/debian/rules.in | 1 + include/Makefile.am | 6 +- include/sunacl.h | 110 ++ include/zfsacl.h | 382 +++++ lib/Makefile.am | 3 + lib/libzfsacl/.gitignore | 3 + lib/libzfsacl/Makefile.am | 20 + lib/libzfsacl/libpyzfsacl.c | 1524 +++++++++++++++++ lib/libzfsacl/setup.py.in | 25 + lib/libzfsacl/sunacl/Makefile.am | 22 + lib/libzfsacl/sunacl/libsunacl.c | 333 ++++ lib/libzfsacl/zfsacl/Makefile.am | 30 + lib/libzfsacl/zfsacl/libzfsacl_impl_freebsd.c | 579 +++++++ lib/libzfsacl/zfsacl/libzfsacl_impl_linux.c | 999 +++++++++++ rpm/generic/zfs.spec.in | 26 +- 19 files changed, 4082 insertions(+), 5 deletions(-) create mode 100644 contrib/debian/openzfs-python3-libzfsacl.install create mode 100644 include/sunacl.h create mode 100644 include/zfsacl.h create mode 100644 lib/libzfsacl/.gitignore create mode 100644 lib/libzfsacl/Makefile.am create mode 100644 lib/libzfsacl/libpyzfsacl.c create mode 100644 lib/libzfsacl/setup.py.in create mode 100644 lib/libzfsacl/sunacl/Makefile.am create mode 100644 lib/libzfsacl/sunacl/libsunacl.c create mode 100644 lib/libzfsacl/zfsacl/Makefile.am create mode 100644 lib/libzfsacl/zfsacl/libzfsacl_impl_freebsd.c create mode 100644 lib/libzfsacl/zfsacl/libzfsacl_impl_linux.c diff --git a/config/deb.am b/config/deb.am index 1379e58c40a8..5c372b5858e7 100644 --- a/config/deb.am +++ b/config/deb.am @@ -66,6 +66,7 @@ deb-utils: deb-local rpm-utils-initramfs pkg9=$${name}-initramfs-$${version}.$${arch}.rpm; \ pkg10=`ls python3-pyzfs-$${version}.noarch.rpm 2>/dev/null`; \ pkg11=`ls pam_zfs_key-$${version}.$${arch}.rpm 2>/dev/null`; \ + pkg12=`ls python3-libzfsacl-$${version}.$${arch}.rpm 2>/dev/null`; \ ## Arguments need to be passed to dh_shlibdeps. Alien provides no mechanism ## to do this, so we install a shim onto the path which calls the real ## dh_shlibdeps with the required arguments. @@ -81,11 +82,11 @@ deb-utils: deb-local rpm-utils-initramfs env "PATH=$${path_prepend}:$${PATH}" \ fakeroot $(ALIEN) --bump=0 --scripts --to-deb --target=$$debarch \ $$pkg1 $$pkg2 $$pkg3 $$pkg4 $$pkg5 $$pkg6 $$pkg7 \ - $$pkg8 $$pkg9 $$pkg10 $$pkg11 || exit 1; \ + $$pkg8 $$pkg9 $$pkg10 $$pkg11 $$pkg12 || exit 1; \ $(RM) $${path_prepend}/dh_shlibdeps; \ rmdir $${path_prepend}; \ $(RM) $$pkg1 $$pkg2 $$pkg3 $$pkg4 $$pkg5 $$pkg6 $$pkg7 \ - $$pkg8 $$pkg9 $$pkg10 $$pkg11; + $$pkg8 $$pkg9 $$pkg10 $$pkg11 $$pkg12; deb: deb-kmod deb-dkms deb-utils diff --git a/contrib/debian/control b/contrib/debian/control index 98beb900d0fa..c540a1944336 100644 --- a/contrib/debian/control +++ b/contrib/debian/control @@ -183,6 +183,17 @@ Description: wrapper for libzfs_core C library (documentation) . This package contains the documentation. +Package: openzfs-python3-libzfsacl +Section: contrib/python +Architecture: linux-any +Depends: ${misc:Depends}, + ${python3:Depends} +Replaces: python3-libzfsacl +Conflicts: python3-libzfsacl +Description: Python module for accessing NFSV41 style ACLs + libzfsacl is cross platform python library for accessing NFSv41 style + ACLs. + Package: openzfs-zfs-dkms Architecture: all Depends: dkms (>> 2.1.1.2-5), @@ -248,6 +259,7 @@ Depends: openzfs-libnvpair3 (= ${binary:Version}), openzfs-libuutil3 (= ${binary:Version}), openzfs-libzfs4 (= ${binary:Version}), openzfs-libzpool5 (= ${binary:Version}), + openzfs-python3-libzfsacl (= ${binary:Version}), python3, ${misc:Depends}, ${shlibs:Depends} diff --git a/contrib/debian/openzfs-python3-libzfsacl.install b/contrib/debian/openzfs-python3-libzfsacl.install new file mode 100644 index 000000000000..e27a98776cfd --- /dev/null +++ b/contrib/debian/openzfs-python3-libzfsacl.install @@ -0,0 +1,4 @@ +usr/lib/python3/dist-packages/libzfsacl-*.egg-info +usr/lib/python3/dist-packages/libzfsacl.cpython-*.so +lib/x86_64-linux-gnu/libzfsacl.so.* +lib/x86_64-linux-gnu/libsunacl.so.* diff --git a/contrib/debian/openzfs-python3-pyzfs.install b/contrib/debian/openzfs-python3-pyzfs.install index 4606faae20a7..ada5eca966a4 100644 --- a/contrib/debian/openzfs-python3-pyzfs.install +++ b/contrib/debian/openzfs-python3-pyzfs.install @@ -1 +1,2 @@ -usr/lib/python3* +usr/lib/python3/dist-packages/libzfs_core +usr/lib/python3/dist-packages/pyzfs-*.egg-info diff --git a/contrib/debian/rules.in b/contrib/debian/rules.in index a3a05efacb50..51ef151baac5 100755 --- a/contrib/debian/rules.in +++ b/contrib/debian/rules.in @@ -135,6 +135,7 @@ override_dh_auto_install: override_dh_python3: dh_python3 -p openzfs-python3-pyzfs + dh_python3 -p openzfs-python3-libzfsacl override_dh_dkms: '$(CURDIR)/scripts/dkms.mkconf' -n $(NAME) -v $(DEB_VERSION_UPSTREAM) -f '$(CURDIR)/scripts/zfs-dkms.dkms' diff --git a/include/Makefile.am b/include/Makefile.am index 5f38f6ac6ddb..ea2e5c7734ea 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -190,8 +190,12 @@ USER_H = \ libzfs_core.h \ libzfsbootenv.h \ libzutil.h \ - thread_pool.h + thread_pool.h \ + zfsacl.h +if BUILD_LINUX +USER_H += sunacl.h +endif if CONFIG_USER libzfsdir = $(includedir)/libzfs diff --git a/include/sunacl.h b/include/sunacl.h new file mode 100644 index 000000000000..2e45c0de7939 --- /dev/null +++ b/include/sunacl.h @@ -0,0 +1,110 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or https://opensource.org/licenses/CDDL-1.0. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2008, 2009 Edward Tomasz Napierała + * Copyright (c) 2022 Andrew Walker + * All rights reserved. + */ + +#ifndef SUNACL_H +#define SUNACL_H extern __attribute__((visibility("default"))) + +#include /* uid_t */ + +/* + * ACL_MAX_ENTRIES from + */ + +typedef struct acl_entry aclent_t; + +typedef struct ace { + uid_t a_who; /* uid or gid */ + uint32_t a_access_mask; /* read,write,... */ + uint16_t a_flags; /* see below */ + uint16_t a_type; /* allow or deny */ +} ace_t; + +/* + * The following are defined for ace_t. + */ +#define ACE_READ_DATA 0x00000001 +#define ACE_LIST_DIRECTORY 0x00000001 +#define ACE_WRITE_DATA 0x00000002 +#define ACE_ADD_FILE 0x00000002 +#define ACE_APPEND_DATA 0x00000004 +#define ACE_ADD_SUBDIRECTORY 0x00000004 +#define ACE_READ_NAMED_ATTRS 0x00000008 +#define ACE_WRITE_NAMED_ATTRS 0x00000010 +#define ACE_EXECUTE 0x00000020 +#define ACE_DELETE_CHILD 0x00000040 +#define ACE_READ_ATTRIBUTES 0x00000080 +#define ACE_WRITE_ATTRIBUTES 0x00000100 +#define ACE_DELETE 0x00010000 +#define ACE_READ_ACL 0x00020000 +#define ACE_WRITE_ACL 0x00040000 +#define ACE_WRITE_OWNER 0x00080000 +#define ACE_SYNCHRONIZE 0x00100000 + +#define ACE_FILE_INHERIT_ACE 0x0001 +#define ACE_DIRECTORY_INHERIT_ACE 0x0002 +#define ACE_NO_PROPAGATE_INHERIT_ACE 0x0004 +#define ACE_INHERIT_ONLY_ACE 0x0008 +#define ACE_SUCCESSFUL_ACCESS_ACE_FLAG 0x0010 +#define ACE_FAILED_ACCESS_ACE_FLAG 0x0020 +#define ACE_IDENTIFIER_GROUP 0x0040 +#define ACE_INHERITED_ACE 0x0080 +#define ACE_OWNER 0x1000 +#define ACE_GROUP 0x2000 +#define ACE_EVERYONE 0x4000 + +#define ACE_ACCESS_ALLOWED_ACE_TYPE 0x0000 +#define ACE_ACCESS_DENIED_ACE_TYPE 0x0001 +#define ACE_SYSTEM_AUDIT_ACE_TYPE 0x0002 +#define ACE_SYSTEM_ALARM_ACE_TYPE 0x0003 + +#define ACE_ALL_PERMS (ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \ + ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_READ_NAMED_ATTRS| \ + ACE_WRITE_NAMED_ATTRS|ACE_EXECUTE|ACE_DELETE_CHILD|ACE_READ_ATTRIBUTES| \ + ACE_WRITE_ATTRIBUTES|ACE_DELETE|ACE_READ_ACL|ACE_WRITE_ACL| \ + ACE_WRITE_OWNER|ACE_SYNCHRONIZE) + +/* + * The following flags are supported by both NFSv4 ACLs and ace_t. + */ +#define ACE_NFSV4_SUP_FLAGS (ACE_FILE_INHERIT_ACE | \ + ACE_DIRECTORY_INHERIT_ACE | \ + ACE_NO_PROPAGATE_INHERIT_ACE | \ + ACE_INHERIT_ONLY_ACE | \ + ACE_IDENTIFIER_GROUP | \ + ACE_INHERITED_ACE) + +#define ACE_TYPE_FLAGS (ACE_OWNER|ACE_GROUP|ACE_EVERYONE|ACE_IDENTIFIER_GROUP) + +/* cmd's to manipulate ace acls. */ +#define ACE_GETACL 4 +#define ACE_SETACL 5 +#define ACE_GETACLCNT 6 + +int acl(const char *path, int cmd, int cnt, void *buf); +int facl(int fd, int cmd, int cnt, void *buf); + +#endif /* SUNACL_H */ diff --git a/include/zfsacl.h b/include/zfsacl.h new file mode 100644 index 000000000000..8157c5771f5b --- /dev/null +++ b/include/zfsacl.h @@ -0,0 +1,382 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or https://opensource.org/licenses/CDDL-1.0. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2022 Andrew Walker + * All rights reserved. + */ + +#ifndef __ZFSACL_H__ +#define __ZFSACL_H__ extern __attribute__((visibility("default"))) + +#include +#include +#include +#include +#include +#include +#include + +#define zfsace4 zfsacl_entry +typedef unsigned int uint_t; + +/* + * BRAND_ACCESS and BRAND_DEFAULT + * values chosen so can convert easily + * to FreeBSD brand POSIX with + * zfsacl_brand_t & ACL_BRAND_POSIX + */ +typedef enum { + ZFSACL_BRAND_UNKNOWN = 0, + ZFSACL_BRAND_NFSV4 = 2, + ZFSACL_BRAND_ACCESS = 3, + ZFSACL_BRAND_DEFAULT = 5, +} zfsacl_brand_t; + +typedef enum { + ZFSACL_UNDEFINED_TAG = 0, + ZFSACL_USER_OBJ = 1, // owner@ in NFSv4 + ZFSACL_GROUP_OBJ = 2, // group@ in NFSv4 + ZFSACL_EVERYONE = 3, // everyone@ -- NFSv4 only + ZFSACL_USER = 11, // named user + ZFSACL_GROUP = 12, // named group + ZFSACL_OTHER = 13, // POSIX1e only + ZFSACL_MASK = 14, // POSIX1e only +} zfsace_who_t; + +typedef enum { + ZFSACL_ENTRY_TYPE_ALLOW = 0, + ZFSACL_ENTRY_TYPE_DENY = 1, + ZFSACL_ENTRY_TYPE_AUDIT = 2, + ZFSACL_ENTRY_TYPE_ALARM = 3, +} zfsace_entry_type_t; + +struct native_acl { + void *data; + size_t datalen; + zfsacl_brand_t brand; +}; + +#ifdef __linux__ +struct zfsacl_entry { uint_t netlong[5]; }; +struct zfsacl { + size_t aclbuf_size; + zfsacl_brand_t brand; + uint_t *aclbuf; +}; +#else +#define _ACL_PRIVATE +#define zfsacl_entry acl_entry +#define zfsacl acl_t_struct +#endif + +typedef struct zfsacl_entry *zfsacl_entry_t; +typedef struct zfsacl *zfsacl_t; + +typedef unsigned int zfsace_flagset_t; +typedef int zfsace_permset_t; +typedef uid_t zfsace_id_t; +typedef unsigned int zfsacl_aclflags_t; + +#define ZFSACL_UNDEFINED_ID ((uid_t)-1) +#define ZFSACL_APPEND_ENTRY -1 +#define ZFSACL_MAX_ENTRIES 64 + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) ((int)(sizeof (a)/sizeof (a[0]))) +#endif + +/* + * this is a warning hack. The idea is to use this everywhere that we + * get the "discarding const" warning from gcc. That doesn't actually + * fix the problem of course, but it means that when we do get to + * cleaning them up we can do it by searching the code for + * discard_const. + * + * It also means that other error types aren't as swamped by the noise + * of hundreds of const warnings, so we are more likely to notice when + * we get new errors. + * + * Please only add more uses of this macro when you find it + * _really_ hard to fix const warnings. Our aim is to eventually use + * this function in only a very few places. + * + * Also, please call this via the discard_const_p() macro interface, as that + * makes the return type safe. + */ +#ifndef discard_const +#define discard_const(ptr) ((void *)((uintptr_t)(ptr))) +#endif + +/* + * Type-safe version of discard_const + */ +#ifndef discard_const_p +#define discard_const_p(type, ptr) ((type *)discard_const(ptr)) +#endif + +typedef struct aclflags2name { + zfsacl_aclflags_t flag; + const char *name; +} aclflags2name_t; + +typedef struct aceperms2name { + zfsace_permset_t perm; + const char *name; + char letter; +} aceperms2name_t; + +typedef struct aceflags2name { + zfsace_flagset_t flag; + const char *name; + char letter; +} aceflags2name_t; + +typedef struct aceswho2name { + zfsace_who_t who; + const char *name; +} aceswho2name_t; + +boolean_t zfsacl_set_fd(int _fd, zfsacl_t _acl); +boolean_t zfsacl_set_file(const char *_path_p, zfsacl_t _acl); +boolean_t zfsacl_set_link(const char *_path_p, zfsacl_t _acl); + +zfsacl_t zfsacl_get_fd(int fd, zfsacl_brand_t _brand); +zfsacl_t zfsacl_get_file(const char *_path_p, zfsacl_brand_t _brand); +zfsacl_t zfsacl_get_link(const char *_path_p, zfsacl_brand_t _brand); + +boolean_t zfsacl_is_trivial(zfsacl_t _acl, boolean_t *trivialp); + +/* + * @brief initialize a new ZFS ACL (for setting on file) + * allocates memory that must be freed + * + * @param[in] _acecnt count of ACEs for new ACL + * @param[in] _brand brand of ACL to allocate + * @return new ACL on succcess, NULL on failure + */ +zfsacl_t zfsacl_init(int _acecnt, zfsacl_brand_t _brand); + +/* + * @brief free an ACL + * + * @param[in] *_acl free an ACL + * @return always succeeds + */ +void zfsacl_free(zfsacl_t *_acl); + +/* + * @brief get branding for specified ACL + * + * @param[in] _acl the ACL from which to get branding info + * @param[out] _brandp the brand (ACCESS, DEFAULT, NFSV4) + * @return B_TRUE on success, B_FALSE on failure + */ +boolean_t zfsacl_get_brand(zfsacl_t _acl, zfsacl_brand_t *_brandp); + +/* + * API to get / set ACL-wide flags + * these are NFSv41-only + */ + +/* + * @brief get ACL-wide flags + * + * @param[in] _acl the ZFS ACL + * @param[out] _paclflags ACL-wide flags + * @return B_TRUE on success, B_FALSE on failure + */ +boolean_t zfsacl_get_aclflags(zfsacl_t _acl, zfsacl_aclflags_t *_paclflags); + +/* + * @brief set ACL-wide flags + * + * @param[in] _acl ZFS ACL to modify + * @param[in] _aclflags flags to set on ACL + * @return B_TRUE on success, B_FALSE on failure + */ +boolean_t zfsacl_set_aclflags(zfsacl_t _acl, zfsacl_aclflags_t _aclflags); + +/* + * @brief get number of ACL entries in ACL + * + * @param[in] _acl the ZFS ACL + * @param[out] _acecnt number of ACEs in ACL. + * @return B_TRUE on success, B_FALSE on failure + */ +boolean_t zfsacl_get_acecnt(zfsacl_t _acl, uint_t *_acecnt); + +/* + * API to get, create, modify, and delete ACL entries + */ + +/* + * @brief create ACL entry at specified index + * special value ZFSACL_APPEND_ENTRY will create new entry + * at end of list. + * + * @param[in] _acl the ZFS ACL to modify + * @param[in] _idx index of where to create new ACL entry + * @param[out] _pentry new ACL entry created + * @return B_TRUE on success, B_FALSE on failure + */ +boolean_t zfsacl_create_aclentry(zfsacl_t _acl, int _idx, + zfsacl_entry_t *_pentry); + +/* + * @brief get ACL entry at specified index + * + * @param[in] _acl ZFS ACL from which to get entry + * @param[in] _idx index of ACL entry to retrieve + * @param[out] _pentry ACL entry retrieved + * @return B_TRUE on success, B_FALSE on failure + */ +boolean_t zfsacl_get_aclentry(zfsacl_t _acl, int _idx, + zfsacl_entry_t *_pentry); + +/* + * @brief remove ACL entry by index + * + * @param[in] _acl ZFS ACL from which to remove entry + * @param[in] _idx index of ACL entry to remove + * @return B_TRUE on success, B_FALSE on failure + */ +boolean_t zfsacl_delete_aclentry(zfsacl_t _acl, int _idx); + + +/* + * @brief convert an ACL to text. Returns malloced string. + * + * @param[in] _acl ZFS ACL + * @return pointer to text form the of specified ACLe + */ +char *zfsacl_to_text(zfsacl_t _acl); + +boolean_t zfsacl_to_native(zfsacl_t _acl, struct native_acl *pnative); + +/* + * ACL entry specific functions + */ +boolean_t zfsace_get_permset(zfsacl_entry_t _entry, + zfsace_permset_t *_pperm); +boolean_t zfsace_get_flagset(zfsacl_entry_t _entry, + zfsace_flagset_t *_pflags); +boolean_t zfsace_get_who(zfsacl_entry_t _entry, zfsace_who_t *pwho, + zfsace_id_t *_paeid); +boolean_t zfsace_get_entry_type(zfsacl_entry_t _entry, + zfsace_entry_type_t *_tp); + +boolean_t zfsace_set_permset(zfsacl_entry_t _entry, zfsace_permset_t _perm); +boolean_t zfsace_set_flagset(zfsacl_entry_t _entry, zfsace_flagset_t _flags); +boolean_t zfsace_set_who(zfsacl_entry_t _entry, zfsace_who_t _who, + zfsace_id_t _aeid); +boolean_t zfsace_set_entry_type(zfsacl_entry_t _entry, zfsace_entry_type_t _tp); + +/* + * NFSv4 ACL-wide flags + * used in zfsacl_get_aclflags() and zfsacl_set_aclflags() + */ + +/* + * ACL flags + */ +#define ZFSACL_AUTO_INHERIT 0x0001 +#define ZFSACL_PROTECTED 0x0002 +#define ZFSACL_DEFAULTED 0x0004 +#define ZFSACL_FLAGS_ALL \ + (ZFSACL_AUTO_INHERIT|ZFSACL_PROTECTED|ZFSACL_DEFAULTED) + +#define ZFSACL_FLAGS_INVALID(flags) (flags & ~ZFSACL_FLAGS_ALL) +/* + * ZFS pflags exposed via ACL call as ACL flags + * valid on get, but not set + */ +#define ZFSACL_IS_TRIVIAL 0x10000 +#define ZFSACL_IS_DIR 0x20000 + +#define ZFSACE_TYPE_INVALID(ae_type) (ae_type > ZFSACL_ENTRY_TYPE_DENY) + +/* + * NFSv4 ACL inheritance flags + * These are not valid if ACL is branded POSIX ACCESS or DEFAULT + */ +#define ZFSACE_FILE_INHERIT 0x00000001 +#define ZFSACE_DIRECTORY_INHERIT 0x00000002 +#define ZFSACE_NO_PROPAGATE_INHERIT 0x00000004 +#define ZFSACE_INHERIT_ONLY 0x00000008 +#define ZFSACE_SUCCESSFUL_ACCESS_ACE_FLAG 0x00000010 +#define ZFSACE_FAILED_ACCESS_ACE_FLAG 0x00000020 +#define ZFSACE_IDENTIFIER_GROUP 0x00000040 +#define ZFSACE_INHERITED_ACE 0x00000080 + +#define ZFSACE_IS_GROUP(flags) (flags & ZFSACE_IDENTIFIER_GROUP) + +#define ZFSACE_FLAG_INVALID(flags) ((flags & 0xFFFFFF30) || ( \ + (flags & ZFSACE_INHERIT_ONLY) && \ + (flags & !(ZFSACE_FILE_INHERIT | ZFSACE_DIRECTORY_INHERIT)))) + +/* + * NFSv4 ACL permissions + */ +#define ZFSACE_READ_DATA 0x00000001 +#define ZFSACE_LIST_DIRECTORY 0x00000001 +#define ZFSACE_WRITE_DATA 0x00000002 +#define ZFSACE_ADD_FILE 0x00000002 +#define ZFSACE_APPEND_DATA 0x00000004 +#define ZFSACE_ADD_SUBDIRECTORY 0x00000004 +#define ZFSACE_READ_NAMED_ATTRS 0x00000008 +#define ZFSACE_WRITE_NAMED_ATTRS 0x00000010 +#define ZFSACE_EXECUTE 0x00000020 +#define ZFSACE_DELETE_CHILD 0x00000040 +#define ZFSACE_READ_ATTRIBUTES 0x00000080 +#define ZFSACE_WRITE_ATTRIBUTES 0x00000100 +#define ZFSACE_DELETE 0x00010000 +#define ZFSACE_READ_ACL 0x00020000 +#define ZFSACE_WRITE_ACL 0x00040000 +#define ZFSACE_WRITE_OWNER 0x00080000 +#define ZFSACE_SYNCHRONIZE 0x00100000 + +#define ZFSACE_FULL_SET (ZFSACE_READ_DATA | ZFSACE_WRITE_DATA | \ + ZFSACE_APPEND_DATA | ZFSACE_READ_NAMED_ATTRS | ZFSACE_WRITE_NAMED_ATTRS | \ + ZFSACE_EXECUTE | ZFSACE_DELETE_CHILD | ZFSACE_READ_ATTRIBUTES | \ + ZFSACE_WRITE_ATTRIBUTES | ZFSACE_DELETE | ZFSACE_READ_ACL | \ + ZFSACE_WRITE_ACL | ZFSACE_WRITE_OWNER | ZFSACE_SYNCHRONIZE) + +#define ZFSACE_MODIFY_SET (ZFSACE_FULL_SET & \ + ~(ZFSACE_WRITE_ACL | ZFSACE_WRITE_OWNER)) + +#define ZFSACE_READ_SET (ZFSACE_READ_DATA | ZFSACE_READ_NAMED_ATTRS | \ + ZFSACE_READ_ATTRIBUTES | ZFSACE_READ_ACL) + +#define ZFSACE_WRITE_SET (ZFSACE_WRITE_DATA | ZFSACE_APPEND_DATA | \ + ZFSACE_WRITE_NAMED_ATTRS | ZFSACE_WRITE_ATTRIBUTES) + +#define ZFSACE_TRAVERSE_SET (ZFSACE_EXECUTE | ZFSACE_READ_NAMED_ATTRS | \ + ZFSACE_READ_ATTRIBUTES | ZFSACE_READ_ACL) + +#define ZFSACE_ACCESS_MASK_INVALID(mask) (mask & ~ZFSACE_FULL_SET) + +#define SPECIAL_WHO_INVALID(who) ( \ + (who != ZFSACL_USER_OBJ) && (who != ZFSACL_USER) && \ + (who != ZFSACL_GROUP_OBJ) && (who != ZFSACL_GROUP) && \ + (who != ZFSACL_EVERYONE)) + +#endif /* __ZFSACL_H__ */ diff --git a/lib/Makefile.am b/lib/Makefile.am index 499ebdaeba9b..c7090e192438 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -64,12 +64,15 @@ include $(srcdir)/%D%/libunicode/Makefile.am include $(srcdir)/%D%/libuutil/Makefile.am include $(srcdir)/%D%/libzfs_core/Makefile.am include $(srcdir)/%D%/libzfs/Makefile.am +include $(srcdir)/%D%/libzfsacl/zfsacl/Makefile.am +include $(srcdir)/%D%/libzfsacl/Makefile.am include $(srcdir)/%D%/libzfsbootenv/Makefile.am include $(srcdir)/%D%/libzpool/Makefile.am include $(srcdir)/%D%/libzstd/Makefile.am include $(srcdir)/%D%/libzutil/Makefile.am if BUILD_LINUX include $(srcdir)/%D%/libefi/Makefile.am +include $(srcdir)/%D%/libzfsacl/sunacl/Makefile.am endif diff --git a/lib/libzfsacl/.gitignore b/lib/libzfsacl/.gitignore new file mode 100644 index 000000000000..15c7e36e74c6 --- /dev/null +++ b/lib/libzfsacl/.gitignore @@ -0,0 +1,3 @@ +build +libzfsacl.egg-info +setup.py diff --git a/lib/libzfsacl/Makefile.am b/lib/libzfsacl/Makefile.am new file mode 100644 index 000000000000..2d63efc27a52 --- /dev/null +++ b/lib/libzfsacl/Makefile.am @@ -0,0 +1,20 @@ +dist_noinst_DATA += \ + %D%/libpyzfsacl.c + +SUBSTFILES += %D%/setup.py + +ALL_LOCAL += libzfsacl-all-local +libzfsacl-all-local: %D%/setup.py + cd %D% && $(PYTHON) setup.py egg_info -e . build + +INSTALL_DATA_HOOKS += libzfsacl-install-data-hook +libzfsacl-install-data-hook: + cd %D% && $(PYTHON) setup.py egg_info -e . install \ + --prefix $(prefix) \ + --root $(DESTDIR)/ \ + --install-lib $(pythonsitedir) \ + --verbose + +CLEAN_LOCAL += libzfsacl-clean-local +libzfsacl-clean-local: + -$(RM) -r %D%/build/ diff --git a/lib/libzfsacl/libpyzfsacl.c b/lib/libzfsacl/libpyzfsacl.c new file mode 100644 index 000000000000..ca0e8ce08e0f --- /dev/null +++ b/lib/libzfsacl/libpyzfsacl.c @@ -0,0 +1,1524 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or https://opensource.org/licenses/CDDL-1.0. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2022 Andrew Walker + * All rights reserved. + */ + +#include +#include +#include + +#define Py_TPFLAGS_HAVE_ITER 0 + +typedef struct { + PyObject_HEAD + boolean_t verbose; + zfsacl_t theacl; +} py_acl; + +typedef struct { + PyObject_HEAD + py_acl *parent_acl; + int idx; + uint_t initial_cnt; + zfsacl_entry_t theace; +} py_acl_entry; + +typedef struct { + PyObject_HEAD + py_acl *acl; + int current_idx; +} py_acl_iterator; + +static void +set_exc_from_errno(const char *func) +{ + PyErr_Format( + PyExc_RuntimeError, + "%s failed: %s", func, strerror(errno)); +} + +static PyObject * +py_acl_iter_next(py_acl_iterator *self) +{ + PyObject *out = NULL; + + out = PyObject_CallMethod( + (PyObject *)self->acl, "get_entry", "i", self->current_idx); + + if (out == NULL) { + if (PyErr_Occurred() == NULL) { + return (NULL); + } + if (PyErr_ExceptionMatches(PyExc_IndexError)) { + /* iteration done */ + PyErr_Clear(); + PyErr_SetNone(PyExc_StopIteration); + return (NULL); + } + /* Some other error occurred */ + return (NULL); + } + + self->current_idx++; + return (out); +} + +static void +py_acl_iter_dealloc(py_acl_iterator *self) +{ + Py_CLEAR(self->acl); + PyObject_Del(self); +} + +PyTypeObject PyACLIterator = { + .tp_name = "ACL Iterator", + .tp_basicsize = sizeof (py_acl_iterator), + .tp_iternext = (iternextfunc)py_acl_iter_next, + .tp_dealloc = (destructor)py_acl_iter_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_iter = PyObject_SelfIter, +}; + +aclflags2name_t aclflag2name[] = { + { ZFSACL_AUTO_INHERIT, "AUTO_INHERIT" }, + { ZFSACL_PROTECTED, "PROTECTED" }, + { ZFSACL_DEFAULTED, "DEFAULTED" }, + { ZFSACL_IS_TRIVIAL, "ACL_IS_TRIVIAL" }, + { ZFSACL_IS_DIR, "IS_DIRECTORY" }, +}; + +static inline PyObject * +aclflag_to_pylist(zfsacl_aclflags_t flags) +{ + int i, err; + PyObject *out = NULL; + + out = Py_BuildValue("[]"); + if (out == NULL) { + return (NULL); + } + + for (i = 0; i < ARRAY_SIZE(aclflag2name); i++) { + PyObject *val = NULL; + + if ((flags & aclflag2name[i].flag) == 0) { + continue; + } + + val = Py_BuildValue("s", aclflag2name[i].name); + if (val == NULL) { + Py_DECREF(out); + return (NULL); + } + + err = PyList_Append(out, val); + Py_XDECREF(val); + if (err == -1) { + Py_XDECREF(out); + return (NULL); + } + } + + return (out); +} + + +aceperms2name_t aceperm2name[] = { + { ZFSACE_READ_DATA, "READ_DATA", 'r' }, + { ZFSACE_LIST_DIRECTORY, "LIST_DIRECTORY", '\0' }, + { ZFSACE_WRITE_DATA, "WRITE_DATA", 'w' }, + { ZFSACE_ADD_FILE, "ADD_FILE", '\0' }, + { ZFSACE_APPEND_DATA, "APPEND_DATA", 'p' }, + { ZFSACE_DELETE, "DELETE", 'd' }, + { ZFSACE_DELETE_CHILD, "DELETE_CHILD", 'D' }, + { ZFSACE_ADD_SUBDIRECTORY, "ADD_SUBDIRECTORY", '\0' }, + { ZFSACE_READ_ATTRIBUTES, "READ_ATTRIBUTES", 'a' }, + { ZFSACE_WRITE_ATTRIBUTES, "WRITE_ATTRIBUTES", 'A' }, + { ZFSACE_READ_NAMED_ATTRS, "READ_NAMED_ATTRS", 'R' }, + { ZFSACE_WRITE_NAMED_ATTRS, "WRITE_NAMED_ATTRS", 'W' }, + { ZFSACE_READ_ACL, "READ_ACL", 'c' }, + { ZFSACE_WRITE_ACL, "WRITE_ACL", 'C' }, + { ZFSACE_WRITE_OWNER, "WRITE_OWNER", 'o' }, + { ZFSACE_SYNCHRONIZE, "SYNCHRONIZE", 's' }, +}; + +static PyObject * +permset_to_pylist(zfsace_permset_t perms) +{ + int i, err; + PyObject *out = NULL; + + out = Py_BuildValue("[]"); + if (out == NULL) { + return (NULL); + } + + for (i = 0; i < ARRAY_SIZE(aceperm2name); i++) { + PyObject *val = NULL; + + if ((perms & aceperm2name[i].perm) == 0) { + continue; + } + + val = Py_BuildValue("s", aceperm2name[i].name); + if (val == NULL) { + Py_DECREF(out); + return (NULL); + } + + err = PyList_Append(out, val); + Py_XDECREF(val); + if (err == -1) { + Py_XDECREF(out); + return (NULL); + } + } + + return (out); +} + +aceflags2name_t _aceflag2name[] = { + { ZFSACE_FILE_INHERIT, "FILE_INHERIT", 'f' }, + { ZFSACE_DIRECTORY_INHERIT, "DIRECTORY_INHERIT", 'd' }, + { ZFSACE_INHERIT_ONLY, "INHERIT_ONLY", 'i' }, + { ZFSACE_NO_PROPAGATE_INHERIT, "NO_PROPAGATE_INHERIT", 'n' }, + { ZFSACE_INHERITED_ACE, "INHERITED", 'I' }, +}; + +static PyObject * +flagset_to_pylist(zfsace_flagset_t flags) +{ + int i, err; + PyObject *out = NULL; + out = Py_BuildValue("[]"); + if (out == NULL) { + return (NULL); + } + + for (i = 0; i < ARRAY_SIZE(_aceflag2name); i++) { + PyObject *val = NULL; + + if ((flags & _aceflag2name[i].flag) == 0) { + continue; + } + + val = Py_BuildValue("s", _aceflag2name[i].name); + if (val == NULL) { + Py_DECREF(out); + return (NULL); + } + + err = PyList_Append(out, val); + Py_XDECREF(val); + if (err == -1) { + Py_XDECREF(out); + return (NULL); + } + } + + return (out); +} + +aceswho2name_t acewho2name[] = { + { ZFSACL_UNDEFINED_TAG, "UNDEFINED" }, + { ZFSACL_USER_OBJ, "USER_OBJ" }, + { ZFSACL_GROUP_OBJ, "GROUP_OBJ" }, + { ZFSACL_EVERYONE, "EVERYONE" }, + { ZFSACL_USER, "USER" }, + { ZFSACL_GROUP, "GROUP" }, + { ZFSACL_OTHER, "OTHER" }, + { ZFSACL_MASK, "MASK" }, +}; + +static PyObject * +whotype_to_pystring(zfsace_who_t whotype) +{ + int i; + PyObject *out = NULL; + + for (i = 0; i < ARRAY_SIZE(acewho2name); i++) { + if (whotype != acewho2name[i].who) { + continue; + } + + out = Py_BuildValue("s", acewho2name[i].name); + if (out == NULL) { + return (NULL); + } + return (out); + } + PyErr_Format(PyExc_ValueError, "%d is an invalid whotype", whotype); + + return (NULL); +} + +static PyObject * +py_ace_new(PyTypeObject *obj, PyObject *args_unused, + PyObject *kwargs_unused) +{ + py_acl_entry *self = NULL; + + self = (py_acl_entry *)obj->tp_alloc(obj, 0); + if (self == NULL) { + return (NULL); + } + self->theace = NULL; + self->parent_acl = NULL; + return ((PyObject *)self); +} + +static int +py_ace_init(PyObject *obj, PyObject *args, PyObject *kwargs) +{ + return (0); +} + +static void +py_ace_dealloc(py_acl_entry *self) +{ + if (self->parent_acl != NULL) { + Py_CLEAR(self->parent_acl); + } + + /* + * memory for ACL entry will be freed when + * ACL is deallocated. + */ + self->theace = NULL; + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static PyObject * +permset_to_basic(zfsace_permset_t perms) +{ + PyObject *out = NULL; + + if (perms == ZFSACE_FULL_SET) { + out = Py_BuildValue("s", "FULL_CONTROL"); + return (out); + } else if (perms == ZFSACE_MODIFY_SET) { + out = Py_BuildValue("s", "MODIFY"); + return (out); + } else if (perms == (ZFSACE_READ_SET | ZFSACE_EXECUTE)) { + out = Py_BuildValue("s", "READ"); + return (out); + } else if (perms == ZFSACE_TRAVERSE_SET) { + out = Py_BuildValue("s", "TRAVERSE"); + return (out); + } + + Py_RETURN_NONE; +} + +static PyObject * +ace_get_permset(PyObject *obj, void *closure) +{ + py_acl_entry *self = (py_acl_entry *)obj; + py_acl *acl = self->parent_acl; + + boolean_t ok; + zfsace_permset_t perms; + PyObject *out = NULL; + + ok = zfsace_get_permset(self->theace, &perms); + if (!ok) { + set_exc_from_errno("zfsace_get_permset()"); + return (NULL); + } + + if (acl && acl->verbose) { + PyObject *permlist = NULL; + PyObject *basic = NULL; + + permlist = permset_to_pylist(perms); + if (permlist == NULL) { + return (NULL); + } + + basic = permset_to_basic(perms); + if (basic == NULL) { + Py_XDECREF(permlist); + return (NULL); + } + + out = Py_BuildValue("{s:I,s:O,s:O}", "raw", perms, + "parsed", permlist, "basic", basic); + + Py_XDECREF(permlist); + Py_XDECREF(basic); + } else { + out = Py_BuildValue("I", perms); + } + + return (out); +} + +static boolean_t +parse_permset(py_acl *acl, PyObject *to_parse, + zfsace_permset_t *permset) +{ + unsigned long py_permset; + + if (!PyLong_Check(to_parse)) + return (B_FALSE); + + py_permset = PyLong_AsUnsignedLong(to_parse); + + if (py_permset == (unsigned long) -1) + return (B_FALSE); + + if (ZFSACE_ACCESS_MASK_INVALID(py_permset)) { + PyErr_SetString(PyExc_ValueError, "invalid flagset."); + return (B_FALSE); + } + + *permset = (zfsace_permset_t)py_permset; + return (B_TRUE); +} + +static int +ace_set_permset(PyObject *obj, PyObject *value, void *closure) +{ + py_acl_entry *self = (py_acl_entry *)obj; + py_acl *acl = self->parent_acl; + boolean_t ok; + zfsace_permset_t permset; + + ok = parse_permset(acl, value, &permset); + if (!ok) { + return (-1); + } + + ok = zfsace_set_permset(self->theace, permset); + if (!ok) { + set_exc_from_errno("zfsace_set_permset()"); + return (-1); + } + return (0); +} + +static PyObject * +flagset_to_basic(zfsace_flagset_t flags) +{ + PyObject *out = NULL; + + /* inherited does not affect consideration of basic */ + flags &= ~ZFSACE_INHERITED_ACE; + + if (flags == (ZFSACE_DIRECTORY_INHERIT | ZFSACE_FILE_INHERIT)) { + out = Py_BuildValue("s", "INHERIT"); + return (out); + } else if (flags == 0) { + out = Py_BuildValue("s", "NO_INHERIT"); + return (out); + } + + Py_RETURN_NONE; +} + +static PyObject * +ace_get_flagset(PyObject *obj, void *closure) +{ + py_acl_entry *self = (py_acl_entry *)obj; + py_acl *acl = self->parent_acl; + boolean_t ok; + zfsace_flagset_t flags; + PyObject *out = NULL; + + ok = zfsace_get_flagset(self->theace, &flags); + if (!ok) { + set_exc_from_errno("zfsace_get_flagset()"); + return (NULL); + } + + if (acl && acl->verbose) { + PyObject *flaglist = NULL; + PyObject *basic = NULL; + flaglist = flagset_to_pylist(flags); + if (flaglist == NULL) { + return (NULL); + } + + basic = flagset_to_basic(flags); + if (basic == NULL) { + Py_XDECREF(flaglist); + return (NULL); + } + + out = Py_BuildValue("{s:I,s:O,s:O}", "raw", flags, "parsed", + flaglist, "basic", basic); + + Py_XDECREF(flaglist); + Py_XDECREF(basic); + } else { + out = Py_BuildValue("I", flags); + } + + return (out); +} + +static boolean_t +parse_flagset(py_acl *acl, PyObject *to_parse, + zfsace_flagset_t *flagset) +{ + unsigned long py_flagset; + + if (!PyLong_Check(to_parse)) + return (B_FALSE); + + py_flagset = PyLong_AsUnsignedLong(to_parse); + + if (py_flagset == (unsigned long) -1) + return (B_FALSE); + + if (ZFSACE_FLAG_INVALID(py_flagset)) { + PyErr_SetString(PyExc_ValueError, "invalid flagset."); + return (B_FALSE); + } + + *flagset = (zfsace_flagset_t)py_flagset; + return (B_TRUE); +} + +static int +ace_set_flagset(PyObject *obj, PyObject *value, void *closure) +{ + py_acl_entry *self = (py_acl_entry *)obj; + py_acl *acl = self->parent_acl; + boolean_t ok; + zfsace_flagset_t flagset; + + ok = parse_flagset(acl, value, &flagset); + if (!ok) { + return (-1); + } + + ok = zfsace_set_flagset(self->theace, flagset); + if (!ok) { + set_exc_from_errno("zfsace_set_flagset()"); + return (-1); + } + return (0); +} + +static PyObject * +verbose_who(zfsace_who_t whotype, zfsace_id_t whoid) +{ + PyObject *pywhotype = NULL; + PyObject *pywhoid = NULL; + PyObject *verbose_whotype = NULL; + PyObject *out = NULL; + + pywhotype = whotype_to_pystring(whotype); + if (pywhotype == NULL) { + return (NULL); + } + + verbose_whotype = Py_BuildValue("{s:I,s:O}", "raw", whotype, + "parsed", pywhotype); + + Py_XDECREF(pywhotype); + + /* + * In future it may make sense to add getpwuid_r / getgrgid_r call here + */ + pywhoid = Py_BuildValue("{s:I,s:I}", "raw", whoid, "parsed", whoid); + + if (pywhoid == NULL) { + Py_XDECREF(verbose_whotype); + return (NULL); + } + + out = Py_BuildValue("{s:O,s:O}", "who_type", verbose_whotype, + "who_id", pywhoid); + + Py_XDECREF(verbose_whotype); + Py_XDECREF(pywhoid); + return (out); +} + +static PyObject * +ace_get_who(PyObject *obj, void *closure) +{ + py_acl_entry *self = (py_acl_entry *)obj; + py_acl *acl = self->parent_acl; + boolean_t ok; + zfsace_who_t whotype; + zfsace_id_t whoid; + PyObject *out = NULL; + + ok = zfsace_get_who(self->theace, &whotype, &whoid); + if (!ok) { + set_exc_from_errno("zfsace_get_who()"); + return (NULL); + } + + if (acl && acl->verbose) { + out = verbose_who(whotype, whoid); + } else { + out = Py_BuildValue("II", whotype, whoid); + } + return (out); +} + +static boolean_t +parse_who(py_acl *acl, PyObject *to_parse, zfsace_who_t *whotype, + zfsace_id_t *whoid) +{ + int pywhotype, pywhoid; + + if (!PyArg_ParseTuple(to_parse, "ii", &pywhotype, &pywhoid)) + return (B_FALSE); + + if (SPECIAL_WHO_INVALID(pywhotype)) { + PyErr_SetString(PyExc_ValueError, "invalid whotype."); + return (B_FALSE); + } + + if ((pywhoid < 0) && (pywhoid != -1)) { + PyErr_SetString(PyExc_ValueError, "invalid id"); + return (B_FALSE); + } + + if ((pywhoid == -1) && + ((pywhotype == ZFSACL_USER) || (pywhotype == ZFSACL_USER))) { + PyErr_SetString(PyExc_ValueError, + "-1 is invalid ID for named entries."); + return (B_FALSE); + } + + if (pywhoid > INT32_MAX) { + PyErr_SetString(PyExc_ValueError, + "ID for named entry is too large."); + return (B_FALSE); + } + + *whotype = (zfsace_who_t)pywhotype; + *whoid = (zfsace_id_t)pywhoid; + + return (B_TRUE); +} + +static int +ace_set_who(PyObject *obj, PyObject *value, void *closure) +{ + py_acl_entry *self = (py_acl_entry *)obj; + py_acl *acl = self->parent_acl; + zfsace_who_t whotype; + zfsace_id_t whoid; + boolean_t ok; + + ok = parse_who(acl, value, &whotype, &whoid); + if (!ok) { + return (-1); + } + + ok = zfsace_set_who(self->theace, whotype, whoid); + if (!ok) { + set_exc_from_errno("zfsace_set_who()"); + return (-1); + } + return (0); +} + +static PyObject * +ace_get_entry_type(PyObject *obj, void *closure) +{ + py_acl_entry *self = (py_acl_entry *)obj; + py_acl *acl = self->parent_acl; + boolean_t ok; + zfsace_entry_type_t entry_type; + PyObject *out = NULL; + + ok = zfsace_get_entry_type(self->theace, &entry_type); + if (!ok) { + set_exc_from_errno("zfsace_get_entry_type()"); + return (NULL); + } + + if (acl && acl->verbose) { + const char *entry_str = NULL; + + switch (entry_type) { + case ZFSACL_ENTRY_TYPE_ALLOW: + entry_str = "ALLOW"; + break; + case ZFSACL_ENTRY_TYPE_DENY: + entry_str = "DENY"; + break; + default: + PyErr_Format(PyExc_ValueError, + "%d is an invalid entry type", entry_type); + return (NULL); + } + out = Py_BuildValue("{s:I,s:s}", "raw", entry_type, "parsed", + entry_str); + } else { + out = Py_BuildValue("I", entry_type); + } + return (out); +} + +static boolean_t +parse_entry_type(py_acl *acl, PyObject *to_parse, + zfsace_entry_type_t *entry_type) +{ + unsigned long py_entry_type; + + + if (!PyLong_Check(to_parse)) + return (B_FALSE); + py_entry_type = PyLong_AsUnsignedLong(to_parse); + + if (py_entry_type == (unsigned long) -1) + return (B_FALSE); + + if (ZFSACE_TYPE_INVALID(py_entry_type)) { + PyErr_SetString(PyExc_ValueError, "invalid ACL entry type."); + return (B_FALSE); + } + + *entry_type = (zfsace_entry_type_t)py_entry_type; + return (B_TRUE); +} + +static int ace_set_entry_type(PyObject *obj, PyObject *value, + void *closure) +{ + py_acl_entry *self = (py_acl_entry *)obj; + py_acl *acl = self->parent_acl; + boolean_t ok; + zfsace_entry_type_t entry_type; + + ok = parse_entry_type(acl, value, &entry_type); + if (!ok) { + return (-1); + } + + ok = zfsace_set_entry_type(self->theace, entry_type); + if (!ok) { + set_exc_from_errno("zfsace_set_entry_type()"); + return (-1); + } + + return (0); +} + +static PyObject * +ace_get_idx(PyObject *obj, void *closure) +{ + py_acl_entry *self = (py_acl_entry *)obj; + return (Py_BuildValue("i", self->idx)); +} + +static PyMethodDef ace_object_methods[] = { + { NULL, NULL, 0, NULL } +}; + +/* BEGIN CSTYLED */ +PyDoc_STRVAR(py_ace_idx__doc__, + "Position of Access control entry in the ACL.\n"); + +PyDoc_STRVAR(py_ace_permset__doc__, +"int : access mask for the access control list entry.\n" +"This should be bitwise or of following values as defined\n" +"in RFC 3530 Section 5.11.2.\n\n" +"Values\n" +"------\n" +"NFSv4 and POSIX1E common permissions:\n" +"zfsacl.PERM_READ_DATA - Permission to read data of the file\n" +"zfsacl.PERM_WRITE_DATA - Permission to modify file's data\n" +"zfsacl.PERM_EXECUTE - Permission to execute a file\n" +"NFSv4 brand specific permissions:\n" +"zfsacl.PERM_LIST_DIRECTORY - Permission to list contents of " +"a directory\n" +"zfsacl.PERM_ADD_FILE - Permission to add a new file to a directory\n" +"zfsacl.PERM_APPEND_DATA - Permission to append data to a file\n" +"zfsacl.PERM_ADD_SUBDIRECTORY - Permission to create a subdirectory " +"to a directory\n" +"zfsacl.PERM_READ_NAMED_ATTRS - Permission to read the named " +"attributes of a file\n" +"zfsacl.PERM_WRITE_NAMED_ATTRS - Permission to write the named " +"attributes of a file\n" +"zfsacl.PERM_DELETE_CHILD - Permission to delete a file or directory " +"within a directorey\n" +"zfsacl.PERM_READ_ATTRIBUTES - Permission to stat() a file\n" +"zfsacl.PERM_WRITE_ATTRIBUTES - Permission to change basic attributes\n" +"zfsacl.PERM_DELETE - Permission to delete the file\n" +"zfsacl.PERM_WRITE_ACL - Permission to write the ACL\n" +"zfsacl.PERM_WRITE_OWNER - Permission to change the owner\n" +"zfsacl.PERM_SYNCHRONIZE - Not Implemented\n\n" +"Warning\n" +"-------\n" +"The exact behavior of these permissions bits may vary depending\n" +"on operating system implementation. Please review relevant OS\n" +"documention and validate the behavior before deploying an access\n" +"control scheme in a production environment.\n" +); + +PyDoc_STRVAR(py_ace_flagset__doc__, +"int : inheritance flags for the access control list entry.\n" +"This should be bitwise or of the following values as defined\n" +"in RFC 5661 Section 6.2.1.4.\n\n" +"Values\n" +"------\n" +"zfsacl.FLAG_FILE_INHERIT - Any non-directory file in any subdirectory\n" +"will get this ACE inherited\n" +"zfsacl.FLAG_DIRECTORY_INHERIT - This ACE will be added to any new " +"subdirectory created in this directory\n" +"zfsacl.FLAG_NO_PROPAGATE_INHERIT - Inheritance of this ACE should stop\n" +"at newly created child directories\n" +"zfsacl.FLAG_INHERIT_ONLY - ACE is not enforced on this directory, but\n" +"will be enforced (cleared) on newly created files and directories\n" +"zfsacl.FLAG_INHERITED - This ace was inherited from a parent directory\n\n" +"Note: flags are not valid for POSIX1E ACLs. The only flag valid for\n" +"files is zfsacl.FLAG_INHERITED, presence of other flags in any ACL entries\n" +"in an ACL will cause setacl attempt on a non-directory file to fail.\n" +); + +PyDoc_STRVAR(py_ace_who__doc__, +"tuple : tuple containing information about to whom the ACL entry applies.\n" +"(, ).\n\n" +"Values - whotype\n" +"----------------\n" +"zfsacl.WHOTYPE_USER_OBJ - The owning user of the file. If this is set, then numeric\n" +"id must be set to -1\n" +"zfsacl.WHOTYPE_GROUP_OBJ - The owning group of the file. If this is set, then\n" +"numeric id must be set to -1\n" +"zfsacl.WHOTYPE_EVERYONE - All users. For NFSv4 ACL brand, this includes the\n" +"file owner and group (as opposed to `other` in conventional POSIX mode)\n" +"zfsacl.WHOTYPE_USER - The numeric ID is a user.\n" +"zfsacl.WHOTYPE_GROUP - The numeric ID is a group.\n" +); + +PyDoc_STRVAR(py_ace_entry_type__doc__, +"int : ACE type. See RFC 5661 Section 6.2.1.1 and relevant operating system\n" +"documentation for more implementation details.\n\n" +"Values\n" +"------\n" +"zfsacl.ENTRY_TYPE_ALLOW - Explicitly grants the access defined in permset\n" +"zfsacl.ENTRY_TYPE_DENY - Explicitly denies the access defined in permset\n" +); +/* END CSTYLED */ + +static PyGetSetDef ace_object_getsetters[] = { + { + .name = discard_const_p(char, "idx"), + .get = (getter)ace_get_idx, + .doc = py_ace_idx__doc__, + }, + { + .name = discard_const_p(char, "permset"), + .get = (getter)ace_get_permset, + .set = (setter)ace_set_permset, + .doc = py_ace_permset__doc__, + }, + { + .name = discard_const_p(char, "flagset"), + .get = (getter)ace_get_flagset, + .set = (setter)ace_set_flagset, + .doc = py_ace_flagset__doc__, + }, + { + .name = discard_const_p(char, "who"), + .get = (getter)ace_get_who, + .set = (setter)ace_set_who, + .doc = py_ace_who__doc__, + }, + { + .name = discard_const_p(char, "entry_type"), + .get = (getter)ace_get_entry_type, + .set = (setter)ace_set_entry_type, + .doc = py_ace_entry_type__doc__, + }, + { .name = NULL } +}; + +static PyTypeObject PyZfsACLEntry = { + .tp_name = "zfsacl.ACLEntry", + .tp_basicsize = sizeof (py_acl_entry), + .tp_methods = ace_object_methods, + .tp_getset = ace_object_getsetters, + .tp_new = py_ace_new, + .tp_init = py_ace_init, + .tp_doc = "An ACL Entry", + .tp_dealloc = (destructor)py_ace_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, +}; + +static PyObject * +py_acl_new(PyTypeObject *obj, PyObject *args_unused, + PyObject *kwargs_unused) +{ + py_acl *self = NULL; + + self = (py_acl *)obj->tp_alloc(obj, 0); + if (self == NULL) { + return (NULL); + } + self->theacl = NULL; + self->verbose = B_FALSE; + return ((PyObject *)self); +} + +static int +py_acl_init(PyObject *obj, PyObject *args, PyObject *kwargs) +{ + py_acl *self = (py_acl *)obj; + zfsacl_t theacl = NULL; + char *kwnames[] = { "fd", "path", "brand", NULL }; + int fd = 0, brand = ZFSACL_BRAND_NFSV4; + char *path = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|isi", kwnames, + &fd, &path, &brand)) { + return (-1); + } + + if (fd != 0) { + theacl = zfsacl_get_fd(fd, brand); + if (theacl == NULL) { + set_exc_from_errno("zfsacl_get_fd()"); + return (-1); + } + } else if (path != NULL) { + theacl = zfsacl_get_file(path, brand); + if (theacl == NULL) { + set_exc_from_errno("zfsacl_get_file()"); + return (-1); + } + } else { + theacl = zfsacl_init(ZFSACL_MAX_ENTRIES, brand); + if (theacl == NULL) { + set_exc_from_errno("zfsacl_get_file()"); + return (-1); + } + } + + if (theacl == NULL) { + set_exc_from_errno("zfsace_set_entry_type()"); + return (-1); + } + + self->theacl = theacl; + + return (0); +} + +static void +py_acl_dealloc(py_acl *self) +{ + if (self->theacl != NULL) { + zfsacl_free(&self->theacl); + self->theacl = NULL; + } + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static PyObject * +acl_get_verbose(PyObject *obj, void *closure) +{ + py_acl *self = (py_acl *)obj; + + if (self->verbose) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + +static int +acl_set_verbose(PyObject *obj, PyObject *value, void *closure) +{ + py_acl *self = (py_acl *)obj; + + if (!PyBool_Check(value)) { + PyErr_SetString(PyExc_TypeError, "value must be boolean."); + return (-1); + } + + self->verbose = (value == Py_True) ? B_TRUE : B_FALSE; + return (0); +} + +static PyObject * +acl_get_flags(PyObject *obj, void *closure) +{ + py_acl *self = (py_acl *)obj; + boolean_t ok; + zfsacl_aclflags_t flags; + PyObject *out = NULL; + + ok = zfsacl_get_aclflags(self->theacl, &flags); + if (!ok) { + set_exc_from_errno("zfsacl_get_aclflags()"); + return (NULL); + } + + out = Py_BuildValue("I", flags); + return (out); +} + +static int +acl_set_flags(PyObject *obj, PyObject *value, void *closure) +{ + py_acl *self = (py_acl *)obj; + long val; + boolean_t ok; + + if (!PyLong_Check(value)) { + PyErr_SetString(PyExc_TypeError, "flags must be integer"); + return (-1); + } + + val = PyLong_AsLong(value); + + if (ZFSACL_FLAGS_INVALID(val)) { + PyErr_SetString(PyExc_ValueError, + "Invalid ACL flags specified"); + return (-1); + } + + ok = zfsacl_set_aclflags(self->theacl, (zfsacl_aclflags_t)val); + if (!ok) { + set_exc_from_errno("zfsacl_set_aclflags()"); + return (-1); + } + + return (0); +} + +static PyObject * +acl_get_brand(PyObject *obj, void *closure) +{ + py_acl *self = (py_acl *)obj; + boolean_t ok; + zfsacl_brand_t brand; + PyObject *out = NULL; + + ok = zfsacl_get_brand(self->theacl, &brand); + if (!ok) { + set_exc_from_errno("zfsacl_get_brand()"); + return (NULL); + } + + out = Py_BuildValue("I", brand); + return (out); +} + +static PyObject * +acl_get_acecnt(PyObject *obj, void *closure) +{ + py_acl *self = (py_acl *)obj; + boolean_t ok; + uint_t acecnt; + PyObject *out = NULL; + + ok = zfsacl_get_acecnt(self->theacl, &acecnt); + if (!ok) { + set_exc_from_errno("zfsacl_get_acecnt()"); + return (NULL); + } + + out = Py_BuildValue("I", acecnt); + return (out); +} + +static boolean_t +initialize_py_ace(py_acl *self, PyObject *in, int idx, + zfsacl_entry_t entry) +{ + py_acl_entry *out = (py_acl_entry *)in; + boolean_t ok; + uint_t acecnt; + + ok = zfsacl_get_acecnt(self->theacl, &acecnt); + if (!ok) { + set_exc_from_errno("zfsacl_get_acecnt()"); + return (B_FALSE); + } + + out->theace = entry; + out->parent_acl = self; + out->initial_cnt = acecnt; + out->idx = (idx == ZFSACL_APPEND_ENTRY) ? (int)acecnt : idx; + Py_INCREF(out->parent_acl); + return (B_TRUE); +} + +static boolean_t +pyargs_get_index(py_acl *self, PyObject *args, int *pidx, + boolean_t required) +{ + int val = -1; + boolean_t ok; + uint_t acecnt; + const char *format = required ? "i" : "|i"; + + if (!PyArg_ParseTuple(args, format, &val)) + return (B_FALSE); + + if (val == -1) { + *pidx = ZFSACL_APPEND_ENTRY; + return (B_TRUE); + } else if (val == 0) { + *pidx = 0; + return (B_TRUE); + } + + if (val < 0) { + PyErr_SetString(PyExc_ValueError, "Index may not be negative"); + return (B_FALSE); + } + + if (val > (ZFSACL_MAX_ENTRIES -1)) { + PyErr_SetString(PyExc_ValueError, + "Index exceeds maximum entries for ACL"); + return (B_FALSE); + } + + ok = zfsacl_get_acecnt(self->theacl, &acecnt); + if (!ok) { + set_exc_from_errno("zfsacl_get_acecnt()"); + return (B_FALSE); + } + + if ((acecnt == 0) || (((uint_t)val) > acecnt -1)) { + PyErr_Format(PyExc_IndexError, + "%ld: index invalid, ACL contains (%u) entries.", val, + acecnt); + return (B_FALSE); + } + + if (val > (ZFSACL_MAX_ENTRIES -1)) { + PyErr_SetString(PyExc_ValueError, + "Index exceeds maximum entries for ACL"); + return (B_FALSE); + } + + *pidx = val; + return (B_TRUE); +} + +/* BEGIN CSTYLED */ +PyDoc_STRVAR(py_acl_create_entry__doc__, +"create_entry(index)\n" +"--\n\n" +"Create a new ACL entry. If index is unspecified then entry\n" +"will be appended to ACL.\n\n" +"Parameters\n" +"----------\n" +"index : int, optional\n" +" Position of new entry in ACL.\n\n" +"Returns\n" +"-------\n" +" new zfsacl.ACLEntry object\n" +); +/* END CSTYLED */ + +static PyObject * +py_acl_create_entry(PyObject *obj, PyObject *args) +{ + py_acl *self = (py_acl *)obj; + boolean_t ok; + int idx; + zfsacl_entry_t entry = NULL; + PyObject *pyentry = NULL; + + ok = pyargs_get_index(self, args, &idx, B_FALSE); + if (!ok) { + return (NULL); + } + + ok = zfsacl_create_aclentry(self->theacl, idx, &entry); + if (!ok) { + set_exc_from_errno("zfsacl_create_aclentry()"); + return (NULL); + } + + pyentry = PyObject_CallFunction((PyObject *)&PyZfsACLEntry, NULL); + ok = initialize_py_ace(self, pyentry, idx, entry); + if (!ok) { + Py_CLEAR(pyentry); + return (NULL); + } + + return ((PyObject *)pyentry); +} + +/* BEGIN CSTYLED */ +PyDoc_STRVAR(py_acl_get_entry__doc__, +"get_entry(index)\n" +"--\n\n" +"Retrieve ACL entry with specified index from ACL.\n\n" +"Parameters\n" +"----------\n" +"index : int\n" +" Position of entry in ACL to be retrieved.\n\n" +"Returns\n" +"-------\n" +" new zfsacl.ACLEntry object\n" +); +/* END CSTYLED */ + +static PyObject * +py_acl_get_entry(PyObject *obj, PyObject *args) +{ + py_acl *self = (py_acl *)obj; + boolean_t ok; + int idx; + zfsacl_entry_t entry = NULL; + PyObject *pyentry = NULL; + + ok = pyargs_get_index(self, args, &idx, B_TRUE); + if (!ok) { + return (NULL); + } + + ok = zfsacl_get_aclentry(self->theacl, idx, &entry); + if (!ok) { + set_exc_from_errno("zfsacl_get_aclentry()"); + return (NULL); + } + + pyentry = PyObject_CallFunction((PyObject *)&PyZfsACLEntry, NULL); + ok = initialize_py_ace(self, pyentry, idx, entry); + if (!ok) { + Py_CLEAR(pyentry); + return (NULL); + } + + return ((PyObject *)pyentry); +} + +/* BEGIN CSTYLED */ +PyDoc_STRVAR(py_acl_delete_entry__doc__, +"delete_entry(index)\n" +"--\n\n" +"Remove the ACL entry specified by index from the ACL.\n\n" +"Parameters\n" +"----------\n" +"index : int\n" +" Position of entry in ACL to be removed.\n\n" +"Returns\n" +"-------\n" +" None\n" +); +/* END CSTYLED */ + +static PyObject * +py_acl_delete_entry(PyObject *obj, PyObject *args) +{ + py_acl *self = (py_acl *)obj; + boolean_t ok; + int idx; + + ok = pyargs_get_index(self, args, &idx, B_TRUE); + if (!ok) { + return (NULL); + } + + ok = zfsacl_delete_aclentry(self->theacl, idx); + if (!ok) { + if ((errno == ERANGE) && (idx == 0)) { + PyErr_SetString(PyExc_ValueError, + "At least one ACL entry is required."); + return (NULL); + } + set_exc_from_errno("zfsacl_delete_aclentry()"); + return (NULL); + } + + Py_RETURN_NONE; +} + +/* BEGIN CSTYLED */ +PyDoc_STRVAR(py_acl_set__doc__, +"setacl(fd=-1, path=None)\n" +"--\n\n" +"Set the acl on either a path or open file.\n" +"Either a path or file must be specified (not both).\n\n" +"Parameters\n" +"----------\n" +"fd : int, optional\n" +" Open file descriptor to use for setting ACL.\n" +"path : string, optional\n" +" Path of file on which to set ACL.\n\n" +"Returns\n" +"-------\n" +" None\n" +); +/* END CSTYLED */ + +static PyObject * +py_acl_set(PyObject *obj, PyObject *args, PyObject *kwargs) +{ + py_acl *self = (py_acl *)obj; + boolean_t ok; + int fd = -1; + const char *path = NULL; + char *kwnames [] = { "fd", "path", NULL }; + + ok = PyArg_ParseTupleAndKeywords(args, kwargs, "|is", kwnames, &fd, + &path); + + if (!ok) { + return (NULL); + } + + if (fd != -1) { + ok = zfsacl_set_fd(fd, self->theacl); + if (!ok) { + set_exc_from_errno("zfsacl_set_fd()"); + return (NULL); + } + } else if (path != NULL) { + ok = zfsacl_set_file(path, self->theacl); + if (!ok) { + set_exc_from_errno("zfsacl_set_file()"); + return (NULL); + } + } else { + PyErr_SetString(PyExc_ValueError, + "`fd` or `path` key is required"); + } + + Py_RETURN_NONE; +} + +static PyObject * +py_acl_iter(PyObject *obj, PyObject *args_unused) +{ + py_acl *self = (py_acl *)obj; + py_acl_iterator *out = NULL; + + out = PyObject_New(py_acl_iterator, &PyACLIterator); + if (out == NULL) { + return (NULL); + } + + out->current_idx = 0; + out->acl = self; + Py_INCREF(self); + return ((PyObject *)out); +} + +static PyMethodDef acl_object_methods[] = { + { + .ml_name = "setacl", + .ml_meth = (PyCFunction)py_acl_set, + .ml_flags = METH_VARARGS|METH_KEYWORDS, + .ml_doc = py_acl_set__doc__ + }, + { + .ml_name = "create_entry", + .ml_meth = py_acl_create_entry, + .ml_flags = METH_VARARGS, + .ml_doc = py_acl_create_entry__doc__ + }, + { + .ml_name = "get_entry", + .ml_meth = py_acl_get_entry, + .ml_flags = METH_VARARGS, + .ml_doc = py_acl_get_entry__doc__ + }, + { + .ml_name = "delete_entry", + .ml_meth = py_acl_delete_entry, + .ml_flags = METH_VARARGS, + .ml_doc = py_acl_delete_entry__doc__ + }, + { NULL, NULL, 0, NULL } +}; + +/* BEGIN CSTYLED */ +PyDoc_STRVAR(py_acl_verbose__doc__, +"bool : Attribute controls whether information about the ACL\n" +"will be printed in verbose format.\n" +); + +PyDoc_STRVAR(py_acl_flags__doc__, +"int : ACL-wide flags. For description of flags see RFC-5661\n" +"section 6.4.2.3 - Automatic Inheritance.\n\n" +"These flags are interpreted by client applications (for example \n" +"Samba) and should be evaluated by applications that recursively\n" +"manage ACLs.\n\n" +"Examples: zfsacl.AUTO_INHERIT, zfsacl.PROTECTED\n" +); + +PyDoc_STRVAR(py_acl_brand__doc__, +"read-only attribute indicating the brand of ACL (POSIX1E or NFSv4).\n" +); + +PyDoc_STRVAR(py_acl_ace_count__doc__, +"read-only attribute indicating the number of ACEs in the ACL.\n" +); +/* END CSTYLED */ + +static PyGetSetDef acl_object_getsetters[] = { + { + .name = discard_const_p(char, "verbose_output"), + .get = (getter)acl_get_verbose, + .set = (setter)acl_set_verbose, + .doc = py_acl_verbose__doc__, + }, + { + .name = discard_const_p(char, "acl_flags"), + .get = (getter)acl_get_flags, + .set = (setter)acl_set_flags, + .doc = py_acl_flags__doc__, + }, + { + .name = discard_const_p(char, "brand"), + .get = (getter)acl_get_brand, + .doc = py_acl_brand__doc__, + }, + { + .name = discard_const_p(char, "ace_count"), + .get = (getter)acl_get_acecnt, + .doc = py_acl_ace_count__doc__, + }, + { .name = NULL } +}; + +static PyTypeObject PyZfsACL = { + .tp_name = "zfsacl.ACL", + .tp_basicsize = sizeof (py_acl), + .tp_methods = acl_object_methods, + .tp_getset = acl_object_getsetters, + .tp_new = py_acl_new, + .tp_init = py_acl_init, + .tp_doc = "An ACL", + .tp_dealloc = (destructor)py_acl_dealloc, + .tp_iter = (getiterfunc)py_acl_iter, + .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_ITER, +}; + +static PyMethodDef acl_module_methods[] = { + { .ml_name = NULL } +}; +#define MODULE_DOC "ZFS ACL python bindings." + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + .m_name = "zfsacl", + .m_doc = MODULE_DOC, + .m_size = -1, + .m_methods = acl_module_methods, +}; + +PyObject* +module_init(void) +{ + PyObject *m = NULL; + m = PyModule_Create(&moduledef); + if (m == NULL) { + fprintf(stderr, "failed to initalize module\n"); + return (NULL); + } + + if (PyType_Ready(&PyZfsACL) < 0) + return (NULL); + + if (PyType_Ready(&PyZfsACLEntry) < 0) + return (NULL); + + /* ZFS ACL branding */ + PyModule_AddIntConstant(m, "BRAND_UNKNOWN", ZFSACL_BRAND_UNKNOWN); + PyModule_AddIntConstant(m, "BRAND_ACCESS", ZFSACL_BRAND_ACCESS); + PyModule_AddIntConstant(m, "BRAND_DEFAULT", ZFSACL_BRAND_DEFAULT); + PyModule_AddIntConstant(m, "BRAND_NFSV4", ZFSACL_BRAND_NFSV4); + + /* ZFS ACL whotypes */ + PyModule_AddIntConstant(m, "WHOTYPE_UNDEFINED", ZFSACL_UNDEFINED_TAG); + PyModule_AddIntConstant(m, "WHOTYPE_USER_OBJ", ZFSACL_USER_OBJ); + PyModule_AddIntConstant(m, "WHOTYPE_GROUP_OBJ", ZFSACL_GROUP_OBJ); + PyModule_AddIntConstant(m, "WHOTYPE_EVERYONE", ZFSACL_EVERYONE); + PyModule_AddIntConstant(m, "WHOTYPE_USER", ZFSACL_USER); + PyModule_AddIntConstant(m, "WHOTYPE_GROUP", ZFSACL_GROUP); + PyModule_AddIntConstant(m, "WHOTYPE_MASK", ZFSACL_MASK); + + /* ZFS ACL entry types */ + PyModule_AddIntConstant(m, "ENTRY_TYPE_ALLOW", ZFSACL_ENTRY_TYPE_ALLOW); + PyModule_AddIntConstant(m, "ENTRY_TYPE_DENY", ZFSACL_ENTRY_TYPE_DENY); + + /* ZFS ACL ACL-wide flags */ + PyModule_AddIntConstant(m, "ACL_AUTO_INHERIT", ZFSACL_AUTO_INHERIT); + PyModule_AddIntConstant(m, "ACL_PROTECTED", ZFSACL_PROTECTED); + PyModule_AddIntConstant(m, "ACL_DEFAULT", ZFSACL_DEFAULTED); + + /* valid on get, but not set */ + PyModule_AddIntConstant(m, "ACL_IS_TRIVIAL", ZFSACL_IS_TRIVIAL); + + /* ZFS ACL inherit flags (NFSv4 only) */ + PyModule_AddIntConstant(m, "FLAG_FILE_INHERIT", ZFSACE_FILE_INHERIT); + PyModule_AddIntConstant(m, "FLAG_DIRECTORY_INHERIT", + ZFSACE_DIRECTORY_INHERIT); + PyModule_AddIntConstant(m, "FLAG_NO_PROPAGATE_INHERIT", + ZFSACE_NO_PROPAGATE_INHERIT); + PyModule_AddIntConstant(m, "FLAG_INHERIT_ONLY", ZFSACE_INHERIT_ONLY); + PyModule_AddIntConstant(m, "FLAG_INHERITED", ZFSACE_INHERITED_ACE); + + /* ZFS ACL permissions */ + /* POSIX1e and NFSv4 */ + PyModule_AddIntConstant(m, "PERM_READ_DATA", ZFSACE_READ_DATA); + PyModule_AddIntConstant(m, "PERM_WRITE_DATA", ZFSACE_WRITE_DATA); + PyModule_AddIntConstant(m, "PERM_EXECUTE", ZFSACE_EXECUTE); + + /* NFSv4 only */ + PyModule_AddIntConstant(m, "PERM_LIST_DIRECTORY", + ZFSACE_LIST_DIRECTORY); + PyModule_AddIntConstant(m, "PERM_ADD_FILE", ZFSACE_ADD_FILE); + PyModule_AddIntConstant(m, "PERM_APPEND_DATA", ZFSACE_APPEND_DATA); + PyModule_AddIntConstant(m, "PERM_ADD_SUBDIRECTORY", + ZFSACE_ADD_SUBDIRECTORY); + PyModule_AddIntConstant(m, "PERM_READ_NAMED_ATTRS", + ZFSACE_READ_NAMED_ATTRS); + PyModule_AddIntConstant(m, "PERM_WRITE_NAMED_ATTRS", + ZFSACE_WRITE_NAMED_ATTRS); + PyModule_AddIntConstant(m, "PERM_DELETE_CHILD", ZFSACE_DELETE_CHILD); + PyModule_AddIntConstant(m, "PERM_READ_ATTRIBUTES", + ZFSACE_READ_ATTRIBUTES); + PyModule_AddIntConstant(m, "PERM_WRITE_ATTRIBUTES", + ZFSACE_WRITE_ATTRIBUTES); + PyModule_AddIntConstant(m, "PERM_DELETE", ZFSACE_DELETE); + PyModule_AddIntConstant(m, "PERM_READ_ACL", ZFSACE_READ_ACL); + PyModule_AddIntConstant(m, "PERM_WRITE_ACL", ZFSACE_WRITE_ACL); + PyModule_AddIntConstant(m, "PERM_WRITE_OWNER", ZFSACE_WRITE_OWNER); + PyModule_AddIntConstant(m, "PERM_SYNCHRONIZE", ZFSACE_SYNCHRONIZE); + PyModule_AddIntConstant(m, "BASIC_PERM_FULL_CONTROL", ZFSACE_FULL_SET); + PyModule_AddIntConstant(m, "BASIC_PERM_MODIFY", ZFSACE_MODIFY_SET); + PyModule_AddIntConstant(m, "BASIC_PERM_READ", + ZFSACE_READ_SET | ZFSACE_EXECUTE); + PyModule_AddIntConstant(m, "BASIC_PERM_TRAVERSE", ZFSACE_TRAVERSE_SET); + + PyModule_AddObject(m, "Acl", (PyObject *)&PyZfsACL); + + return (m); +} + +PyMODINIT_FUNC +PyInit_libzfsacl(void) +{ + return (module_init()); +} diff --git a/lib/libzfsacl/setup.py.in b/lib/libzfsacl/setup.py.in new file mode 100644 index 000000000000..28693058486f --- /dev/null +++ b/lib/libzfsacl/setup.py.in @@ -0,0 +1,25 @@ +from setuptools import setup, Extension, find_packages +import os +import sys + +srcdir = '@abs_top_srcdir@/lib/libzfsacl' + +topsrcdir = '@abs_top_srcdir@' +incdir = [topsrcdir, topsrcdir + '/include', topsrcdir + '/lib/libspl/include'] +if sys.platform.startswith('linux'): + src = ['zfsacl/libzfsacl_impl_linux.c', 'libpyzfsacl.c'] +else: + src = ['zfsacl/libzfsacl_impl_freebsd.c', 'libpyzfsacl.c'] + +libzfsacl_mod = Extension('libzfsacl', include_dirs=incdir, sources=src) +setup( + name='libzfsacl', + version='@VERSION@', + description='ACL wrapper library for acessing NFSv4 ACLs on Linux/FreeBSD', + ext_modules=[libzfsacl_mod], + packages=find_packages(where=srcdir), + package_dir={"": os.path.relpath(srcdir)}, + include_package_data=True, + python_requires='>=3.6,<4', + zip_safe=False, +) diff --git a/lib/libzfsacl/sunacl/Makefile.am b/lib/libzfsacl/sunacl/Makefile.am new file mode 100644 index 000000000000..88a5760919c4 --- /dev/null +++ b/lib/libzfsacl/sunacl/Makefile.am @@ -0,0 +1,22 @@ +libsunacl_la_CFLAGS = $(AM_CFLAGS) $(LIBRARY_CFLAGS) + +lib_LTLIBRARIES += libsunacl.la +CPPCHECKTARGETS += libsunacl.la + +libsunacl_la_CPPFLAGS = $(AM_CPPFLAGS) + +dist_libsunacl_la_SOURCES = \ + %D%/libsunacl.c + +libsunacl_la_LIBADD = \ + libzfsacl.la \ + libzfs.la + +libsunacl_la_LDFLAGS = + +if !ASAN_ENABLED +libsunacl_la_LDFLAGS += -Wl,-z,defs +endif + +libsunacl_la_LDFLAGS += -version-info 1:0:0 + diff --git a/lib/libzfsacl/sunacl/libsunacl.c b/lib/libzfsacl/sunacl/libsunacl.c new file mode 100644 index 000000000000..b16ab69a3aab --- /dev/null +++ b/lib/libzfsacl/sunacl/libsunacl.c @@ -0,0 +1,333 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or https://opensource.org/licenses/CDDL-1.0. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2008, 2009 Edward Tomasz Napierała + * Copyright (c) 2022 Andrew Walker + * All rights reserved. + */ + +#include +#include +#include +#include +#include + + +#define ACE_GETACL 4 +#define ACE_SETACL 5 +#define ACE_GETACLCNT 6 + +static int +acl_from_aces(zfsacl_t aclp, const ace_t *aces, int nentries) +{ + int i; + const ace_t *ace = NULL; + boolean_t ok; + + if (nentries > ZFSACL_MAX_ENTRIES) { + /* + * I believe it may happen only when moving a pool + * from SunOS to FreeBSD. + */ + printf("acl_from_aces: ZFS ACL too big to fit " + "into 'struct acl'; returning EINVAL.\n"); + return (EINVAL); + } + + for (i = 0; i < nentries; i++) { + zfsace_permset_t permset = 0; + zfsace_flagset_t flagset = 0; + zfsace_who_t whotype = 0; + zfsace_id_t whoid = ZFSACL_UNDEFINED_ID; + zfsace_entry_type_t entry_type = 0; + zfsacl_entry_t entry = NULL; + + ok = zfsacl_get_aclentry(aclp, i, &entry); + if (!ok) { + return (errno); + } + + ace = &(aces[i]); + + permset = ace->a_access_mask; + flagset = ace->a_flags; + + if (ace->a_flags & ACE_OWNER) { + whotype = ZFSACL_USER_OBJ; + } else if (ace->a_flags & ACE_GROUP) { + whotype = ZFSACL_GROUP_OBJ; + flagset |= ZFSACE_IDENTIFIER_GROUP; + } else if (ace->a_flags & ACE_EVERYONE) { + whotype = ZFSACL_EVERYONE; + } else if (ace->a_flags & ACE_IDENTIFIER_GROUP) { + whotype = ZFSACL_GROUP; + flagset |= ZFSACE_IDENTIFIER_GROUP; + } else { + whotype = ZFSACL_USER; + } + + if (whotype == ZFSACL_USER || whotype == ZFSACL_GROUP) + whoid = ace->a_who; + + switch (ace->a_type) { + case ACE_ACCESS_ALLOWED_ACE_TYPE: + entry_type = ZFSACL_ENTRY_TYPE_ALLOW; + break; + case ACE_ACCESS_DENIED_ACE_TYPE: + entry_type = ZFSACL_ENTRY_TYPE_DENY; + break; + case ACE_SYSTEM_AUDIT_ACE_TYPE: + entry_type = ZFSACL_ENTRY_TYPE_AUDIT; + break; + case ACE_SYSTEM_ALARM_ACE_TYPE: + entry_type = ZFSACL_ENTRY_TYPE_ALARM; + break; + default: + abort(); + } + + ok = zfsace_set_permset(entry, permset); + if (!ok) { + return (errno); + } + ok = zfsace_set_flagset(entry, flagset); + if (!ok) { + return (errno); + } + ok = zfsace_set_who(entry, whotype, whoid); + if (!ok) { + return (errno); + } + ok = zfsace_set_entry_type(entry, entry_type); + if (!ok) { + return (errno); + } + } + + return (0); +} + +static int +aces_from_acl(ace_t *aces, int *nentries, zfsacl_t aclp) +{ + int i; + uint_t acecnt; + ace_t *ace; + boolean_t ok; + + ok = zfsacl_get_acecnt(aclp, &acecnt); + if (!ok) { + return (errno); + } + + memset(aces, 0, sizeof (*aces) * acecnt); + *nentries = (int)acecnt; + + for (i = 0; i < (int)acecnt; i++) { + zfsace_permset_t permset = 0; + zfsace_flagset_t flagset = 0; + zfsace_who_t whotype = 0; + zfsace_id_t whoid = ZFSACL_UNDEFINED_ID; + zfsace_entry_type_t entry_type = 0; + zfsacl_entry_t entry = NULL; + + ok = zfsacl_get_aclentry(aclp, i, &entry); + if (!ok) { + return (errno); + } + ok = zfsace_get_permset(entry, &permset); + if (!ok) { + return (errno); + } + ok = zfsace_get_flagset(entry, &flagset); + if (!ok) { + return (errno); + } + ok = zfsace_get_who(entry, &whotype, &whoid); + if (!ok) { + return (errno); + } + ok = zfsace_get_entry_type(entry, &entry_type); + if (!ok) { + return (errno); + } + + ace = &(aces[i]); + + ace->a_who = whoid; + ace->a_access_mask = permset; + ace->a_flags = flagset; + + if (whotype == ZFSACL_USER_OBJ) + ace->a_flags |= ACE_OWNER; + else if (whotype == ZFSACL_GROUP_OBJ) + ace->a_flags |= (ACE_GROUP | ACE_IDENTIFIER_GROUP); + else if (whotype == ZFSACL_GROUP) + ace->a_flags |= ACE_IDENTIFIER_GROUP; + else if (whotype == ZFSACL_EVERYONE) + ace->a_flags |= ACE_EVERYONE; + + switch (entry_type) { + case ZFSACL_ENTRY_TYPE_ALLOW: + ace->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE; + break; + case ZFSACL_ENTRY_TYPE_DENY: + ace->a_type = ACE_ACCESS_DENIED_ACE_TYPE; + break; + case ZFSACL_ENTRY_TYPE_ALARM: + ace->a_type = ACE_SYSTEM_ALARM_ACE_TYPE; + break; + case ZFSACL_ENTRY_TYPE_AUDIT: + ace->a_type = ACE_SYSTEM_AUDIT_ACE_TYPE; + break; + default: + abort(); + } + } + + return (0); +} + +static int +xacl(const char *path, int fd, int cmd, int cnt, void *buf) +{ + int error, nentries; + zfsacl_t aclp = NULL; + uint_t acecnt; + boolean_t ok; + + switch (cmd) { + case ACE_SETACL: + if (buf == NULL || cnt <= 0) { + errno = EINVAL; + return (-1); + } + + if (cnt >= ZFSACL_MAX_ENTRIES) { + errno = ENOSPC; + return (-1); + } + + aclp = zfsacl_init(cnt, ZFSACL_BRAND_NFSV4); + if (aclp == NULL) { + return (-1); + } + + error = acl_from_aces(aclp, buf, cnt); + if (error) { + zfsacl_free(&aclp); + errno = EIO; + return (-1); + } + + /* + * Ugly hack to make sure we don't trip sanity check at + * lib/libc/posix1e/acl_branding.c: + * _acl_type_not_valid_for_acl(). + */ + if (path != NULL) + ok = zfsacl_set_file(path, aclp); + else + ok = zfsacl_set_fd(fd, aclp); + if (error) { + if (errno == EOPNOTSUPP || errno == EINVAL) + errno = ENOSYS; + zfsacl_free(&aclp); + return (-1); + } + + zfsacl_free(&aclp); + return (0); + + case ACE_GETACL: + if (buf == NULL) { + errno = EINVAL; + return (-1); + } + + if (path != NULL) + aclp = zfsacl_get_file(path, ZFSACL_BRAND_NFSV4); + else + aclp = zfsacl_get_fd(fd, ZFSACL_BRAND_NFSV4); + if (aclp == NULL) { + if (errno == EOPNOTSUPP || errno == EINVAL) + errno = ENOSYS; + return (-1); + } + + ok = zfsacl_get_acecnt(aclp, &acecnt); + if (!ok || acecnt > (uint_t)cnt) { + zfsacl_free(&aclp); + errno = ENOSPC; + return (-1); + } + + error = aces_from_acl(buf, &nentries, aclp); + zfsacl_free(&aclp); + if (error) { + errno = EIO; + return (-1); + } + + return (nentries); + + case ACE_GETACLCNT: + if (path != NULL) + aclp = zfsacl_get_file(path, ZFSACL_BRAND_NFSV4); + else + aclp = zfsacl_get_fd(fd, ZFSACL_BRAND_NFSV4); + if (aclp == NULL) { + if (errno == EOPNOTSUPP || errno == EINVAL) + errno = ENOSYS; + return (-1); + } + + ok = zfsacl_get_acecnt(aclp, &acecnt); + if (!ok) { + return (-1); + } + nentries = acecnt; + zfsacl_free(&aclp); + return (nentries); + + default: + errno = EINVAL; + return (-1); + } +} + +int +acl(const char *path, int cmd, int cnt, void *buf) +{ + if (path == NULL) { + errno = EINVAL; + return (-1); + } + + return (xacl(path, -1, cmd, cnt, buf)); +} + +int +facl(int fd, int cmd, int cnt, void *buf) +{ + return (xacl(NULL, fd, cmd, cnt, buf)); +} diff --git a/lib/libzfsacl/zfsacl/Makefile.am b/lib/libzfsacl/zfsacl/Makefile.am new file mode 100644 index 000000000000..0acf91e09636 --- /dev/null +++ b/lib/libzfsacl/zfsacl/Makefile.am @@ -0,0 +1,30 @@ +libzfsacl_la_CFLAGS = $(AM_CFLAGS) $(LIBRARY_CFLAGS) -fPIC + +lib_LTLIBRARIES += libzfsacl.la +CPPCHECKTARGETS += libzfsacl.la + +libzfsacl_la_CPPFLAGS = $(AM_CPPFLAGS) + +if BUILD_LINUX +dist_libzfsacl_la_SOURCES = \ + %D%/libzfsacl_impl_linux.c +endif + +if BUILD_FREEBSD +dist_libzfsacl_la_SOURCES = \ + %D%/libzfsacl_impl_freebsd.c +endif + +libzfsacl_la_LIBADD = \ + libzfs.la \ + libspl.la + +libzfsacl_la_LIBADD += $(LTLIBINTL) + +libzfsacl_la_LDFLAGS = + +if !ASAN_ENABLED +libzfsacl_la_LDFLAGS += -Wl,-z,defs +endif + +libzfsacl_la_LDFLAGS += -version-info 1:0:0 diff --git a/lib/libzfsacl/zfsacl/libzfsacl_impl_freebsd.c b/lib/libzfsacl/zfsacl/libzfsacl_impl_freebsd.c new file mode 100644 index 000000000000..601bb95037b8 --- /dev/null +++ b/lib/libzfsacl/zfsacl/libzfsacl_impl_freebsd.c @@ -0,0 +1,579 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or https://opensource.org/licenses/CDDL-1.0. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2022 Andrew Walker + * All rights reserved. + */ + +#include +#include "/usr/include/sys/acl.h" + +#define BSDACE(zfsace) ((acl_entry_t)zfsace) +#define BSDACL(zfsacl) ((acl_t)zfsacl) +#define ZFSACL(bsdacl) ((zfsacl_t)bsdacl) + +static const struct { + acl_flag_t bsdflag; + zfsace_flagset_t nfs4flag; +} bsdflag2nfs4flag[] = { + { ACL_ENTRY_FILE_INHERIT, ZFSACE_FILE_INHERIT }, + { ACL_ENTRY_DIRECTORY_INHERIT, ZFSACE_DIRECTORY_INHERIT }, + { ACL_ENTRY_NO_PROPAGATE_INHERIT, ZFSACE_NO_PROPAGATE_INHERIT }, + { ACL_ENTRY_INHERIT_ONLY, ZFSACE_INHERIT_ONLY }, + { ACL_ENTRY_INHERITED, ZFSACE_INHERITED_ACE }, +}; + +static const struct { + acl_perm_t bsdperm; + zfsace_permset_t nfs4perm; +} bsdperm2nfs4perm[] = { + { ACL_READ_DATA, ZFSACE_READ_DATA }, + { ACL_WRITE_DATA, ZFSACE_WRITE_DATA }, + { ACL_APPEND_DATA, ZFSACE_APPEND_DATA }, + { ACL_READ_NAMED_ATTRS, ZFSACE_READ_NAMED_ATTRS }, + { ACL_WRITE_NAMED_ATTRS, ZFSACE_WRITE_NAMED_ATTRS }, + { ACL_EXECUTE, ZFSACE_EXECUTE }, + { ACL_DELETE_CHILD, ZFSACE_DELETE_CHILD }, + { ACL_READ_ATTRIBUTES, ZFSACE_READ_ATTRIBUTES }, + { ACL_WRITE_ATTRIBUTES, ZFSACE_WRITE_ATTRIBUTES }, + { ACL_DELETE, ZFSACE_DELETE }, + { ACL_READ_ACL, ZFSACE_READ_ACL }, + { ACL_WRITE_ACL, ZFSACE_WRITE_ACL }, + { ACL_WRITE_OWNER, ZFSACE_WRITE_OWNER }, + { ACL_SYNCHRONIZE, ZFSACE_SYNCHRONIZE }, +}; + +static inline int +CONV_BRAND(zfsacl_brand_t brand_in) +{ + return (brand_in & ACL_BRAND_POSIX) ^ (brand_in & ACL_BRAND_NFS4); +} + +static inline void +BSD_BRAND(acl_t _acl) +{ + _acl->ats_brand = CONV_BRAND(_acl->ats_brand); +} + +static inline acl_type_t +brand_to_type(zfsacl_brand_t _brand) +{ + acl_type_t out = 0; + + switch (_brand) { + case ZFSACL_BRAND_NFSV4: + out = ACL_TYPE_NFS4; + break; + case ZFSACL_BRAND_ACCESS: + out = ACL_TYPE_ACCESS; + break; + case ZFSACL_BRAND_DEFAULT: + out = ACL_TYPE_DEFAULT; + break; + default: + fprintf(stderr, "0x%08x: invalid ACL brand\n", _brand); + break; + }; + + return (out); +} + +zfsacl_t +zfsacl_init(int _acecnt, zfsacl_brand_t _brand) +{ + acl_t out = NULL; + + out = acl_init(_acecnt); + if (out == NULL) { + return (NULL); + } + out->ats_brand = _brand; + return (ZFSACL(out)); +} + +void +zfsacl_free(zfsacl_t *_acl) +{ + acl_t acl = BSDACL(*_acl); + acl_free(acl); + *_acl = NULL; +} + +zfsacl_t +zfsacl_get_fd(int _fd, zfsacl_brand_t _brand) +{ + acl_t out = NULL; + + out = acl_get_fd_np(_fd, brand_to_type(_brand)); + if (out == NULL) { + return (NULL); + } + out->ats_brand = _brand; + return (ZFSACL(out)); +} + +zfsacl_t +zfsacl_get_file(const char *_path_p, zfsacl_brand_t _brand) +{ + acl_t out = NULL; + + out = acl_get_file(_path_p, brand_to_type(_brand)); + if (out == NULL) { + return (NULL); + } + out->ats_brand = _brand; + return (ZFSACL(out)); +} + +zfsacl_t +zfsacl_get_link(const char *_path_p, zfsacl_brand_t _brand) +{ + acl_t out = NULL; + + out = acl_get_link_np(_path_p, brand_to_type(_brand)); + if (out == NULL) { + return (NULL); + } + out->ats_brand = _brand; + return (ZFSACL(out)); +} + +boolean_t +zfsacl_set_fd(int _fd, zfsacl_t _acl) +{ + acl_t acl = BSDACL(_acl); + zfsacl_brand_t saved = acl->ats_brand; + int err; + + BSD_BRAND(acl); + err = acl_set_fd_np(_fd, acl, brand_to_type(saved)); + acl->ats_brand = saved; + + return (err ? B_FALSE : B_TRUE); +} + +boolean_t +zfsacl_set_file(const char *_path_p, zfsacl_t _acl) +{ + acl_t acl = BSDACL(_acl); + zfsacl_brand_t saved = acl->ats_brand; + int err; + + BSD_BRAND(acl); + err = acl_set_file(_path_p, brand_to_type(saved), acl); + acl->ats_brand = saved; + + return (err ? B_FALSE : B_TRUE); +} + +boolean_t +zfsacl_set_link(const char *_path_p, zfsacl_t _acl) +{ + acl_t acl = BSDACL(_acl); + zfsacl_brand_t saved = acl->ats_brand; + int err; + + BSD_BRAND(acl); + err = acl_set_link_np(_path_p, brand_to_type(saved), acl); + acl->ats_brand = saved; + + return (err ? B_FALSE : B_TRUE); +} + +boolean_t +zfsacl_get_brand(zfsacl_t _acl, zfsacl_brand_t *brandp) +{ + acl_t acl = BSDACL(_acl); + *brandp = acl->ats_brand; + return (B_TRUE); +} + +boolean_t +zfsacl_get_aclflags(zfsacl_t _acl, zfsacl_aclflags_t *pflags) +{ + /* + * TODO: FreeBSD still needs to expose ACL flags + * for now we synthesize the PROTECTED flag so that + * Security Descriptor flags can be presented correctly + * to clients. + */ + acl_t acl = BSDACL(_acl); + unsigned int cnt; + zfsace_flagset_t flags_out = 0; + acl_flag_t flags; + + for (cnt = 0; cnt < acl->ats_acl.acl_cnt; cnt++) { + flags = acl->ats_acl.acl_entry[cnt].ae_flags; + if ((flags & ACL_ENTRY_INHERITED) == 0) + continue; + + flags_out = ZFSACL_PROTECTED; + break; + } + + *pflags = flags_out; + return (B_TRUE); +} + + +boolean_t +zfsacl_set_aclflags(zfsacl_t _acl, zfsacl_aclflags_t flags) +{ + /* + * TODO: FreeBSD still needs to expose ACL flags + */ + (void) _acl; + (void) flags; + errno = EOPNOTSUPP; + return (B_FALSE); +} + +boolean_t +zfsacl_get_acecnt(zfsacl_t _acl, uint_t *_acecnt) +{ + acl_t acl = BSDACL(_acl); + *_acecnt = acl->ats_acl.acl_cnt; + return (B_TRUE); +} + +static boolean_t +validate_entry_idx(zfsacl_t _acl, uint_t _idx) +{ + uint_t acecnt; + boolean_t ok; + + ok = zfsacl_get_acecnt(_acl, &acecnt); + if (!ok) { + return (B_FALSE); + } + + if ((_idx + 1) > acecnt) { + errno = E2BIG; + return (B_FALSE); + } + + return (B_TRUE); +} + +boolean_t +zfsacl_create_aclentry(zfsacl_t _acl, int _idx, zfsacl_entry_t *_pentry) +{ + acl_t acl = BSDACL(_acl); + int err; + zfsacl_brand_t saved = acl->ats_brand; + acl_entry_t new_entry = NULL; + + BSD_BRAND(acl); + if (_idx == ZFSACL_APPEND_ENTRY) { + err = acl_create_entry(&acl, &new_entry); + } else { + err = acl_create_entry_np(&acl, &new_entry, _idx); + } + + acl->ats_brand = saved; + + if (err) { + return (B_FALSE); + } + + *_pentry = new_entry; + return (B_TRUE); +} + +boolean_t +zfsacl_get_aclentry(zfsacl_t _acl, int _idx, zfsacl_entry_t *_pentry) +{ + acl_t acl = BSDACL(_acl); + acl_entry_t entry = NULL; + + if (!validate_entry_idx(_acl, _idx)) { + return (B_FALSE); + } + + entry = &acl->ats_acl.acl_entry[_idx]; + *_pentry = entry; + return (B_TRUE); +} + +boolean_t +zfsacl_delete_aclentry(zfsacl_t _acl, int _idx) +{ + acl_t acl = BSDACL(_acl); + int err; + zfsacl_brand_t saved = acl->ats_brand; + + BSD_BRAND(acl); + + err = acl_delete_entry_np(acl, _idx); + acl->ats_brand = saved; + + return (err ? B_FALSE : B_TRUE); +} + +boolean_t +zfsace_get_permset(zfsacl_entry_t _entry, zfsace_permset_t *_pperm) +{ + acl_entry_t entry = BSDACE(_entry); + zfsace_permset_t perm = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(bsdperm2nfs4perm); i++) { + if (entry->ae_perm & bsdperm2nfs4perm[i].bsdperm) { + perm |= bsdperm2nfs4perm[i].nfs4perm; + } + } + + *_pperm = perm; + return (B_TRUE); +} + +boolean_t +zfsace_get_flagset(zfsacl_entry_t _entry, zfsace_flagset_t *_pflags) +{ + acl_entry_t entry = BSDACE(_entry); + zfsace_flagset_t flags = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(bsdflag2nfs4flag); i++) { + if (entry->ae_flags & bsdflag2nfs4flag[i].bsdflag) { + flags |= bsdflag2nfs4flag[i].nfs4flag; + } + } + + if (entry->ae_tag & (ACL_GROUP_OBJ | ACL_GROUP)) { + flags |= ZFSACE_IDENTIFIER_GROUP; + } + + *_pflags = flags; + return (B_TRUE); +} + +boolean_t +zfsace_get_who(zfsacl_entry_t _entry, zfsace_who_t *pwho, zfsace_id_t *_paeid) +{ + acl_entry_t entry = BSDACE(_entry); + zfsace_who_t whotype; + zfsace_id_t whoid = ZFSACL_UNDEFINED_ID; + + switch (entry->ae_tag) { + case ACL_UNDEFINED_TAG: + whotype = ZFSACL_UNDEFINED_TAG; + break; + case ACL_USER_OBJ: + whotype = ZFSACL_USER_OBJ; + break; + case ACL_GROUP_OBJ: + whotype = ZFSACL_GROUP_OBJ; + break; + case ACL_EVERYONE: + whotype = ZFSACL_EVERYONE; + break; + case ACL_MASK: + whotype = ZFSACL_MASK; + break; + case ACL_OTHER: + whotype = ZFSACL_MASK; + break; + case ACL_USER: + whotype = ZFSACL_USER; + whoid = entry->ae_id; + break; + case ACL_GROUP: + whotype = ZFSACL_GROUP; + whoid = entry->ae_id; + break; + default: + abort(); + }; + + *pwho = whotype; + *_paeid = whoid; + return (B_TRUE); +} + +boolean_t +zfsace_get_entry_type(zfsacl_entry_t _entry, zfsace_entry_type_t *_tp) +{ + acl_entry_t entry = BSDACE(_entry); + zfsace_entry_type_t etype; + + switch (entry->ae_entry_type) { + case ACL_ENTRY_TYPE_ALLOW: + etype = ZFSACL_ENTRY_TYPE_ALLOW; + break; + case ACL_ENTRY_TYPE_DENY: + etype = ZFSACL_ENTRY_TYPE_DENY; + break; + case ACL_ENTRY_TYPE_AUDIT: + etype = ZFSACL_ENTRY_TYPE_AUDIT; + break; + case ACL_ENTRY_TYPE_ALARM: + etype = ZFSACL_ENTRY_TYPE_AUDIT; + break; + default: + abort(); + }; + + *_tp = etype; + return (B_TRUE); +} + +boolean_t +zfsace_set_permset(zfsacl_entry_t _entry, zfsace_permset_t _permset) +{ + acl_entry_t entry = BSDACE(_entry); + int permset = 0; + int i, err; + + for (i = 0; i < ARRAY_SIZE(bsdperm2nfs4perm); i++) { + if (_permset & bsdperm2nfs4perm[i].nfs4perm) { + permset |= bsdperm2nfs4perm[i].bsdperm; + } + } + + err = acl_set_permset(entry, &permset); + return (err ? B_FALSE : B_TRUE); +} + +boolean_t +zfsace_set_flagset(zfsacl_entry_t _entry, zfsace_flagset_t _flagset) +{ + acl_entry_t entry = BSDACE(_entry); + acl_flag_t flags = 0; + int i, err; + + for (i = 0; i < ARRAY_SIZE(bsdflag2nfs4flag); i++) { + if (_flagset & bsdflag2nfs4flag[i].nfs4flag) { + flags |= bsdflag2nfs4flag[i].bsdflag; + } + } + + err = acl_set_flagset_np(entry, &flags); + return (err ? B_FALSE : B_TRUE); +} + +boolean_t +zfsace_set_who(zfsacl_entry_t _entry, zfsace_who_t _who, zfsace_id_t _aeid) +{ + acl_entry_t entry = BSDACE(_entry); + uid_t id = ACL_UNDEFINED_ID; + acl_tag_t tag; + int err; + + switch (_who) { + case ZFSACL_USER_OBJ: + tag = ACL_USER_OBJ; + break; + case ZFSACL_GROUP_OBJ: + tag = ACL_GROUP_OBJ; + break; + case ZFSACL_EVERYONE: + tag = ACL_EVERYONE; + break; + case ZFSACL_OTHER: + tag = ACL_OTHER; + break; + case ZFSACL_MASK: + tag = ACL_MASK; + break; + case ZFSACL_USER: + tag = ACL_USER; + id = _aeid; + break; + case ZFSACL_GROUP: + tag = ACL_GROUP; + id = _aeid; + break; + default: + abort(); + }; + + err = acl_set_tag_type(entry, tag); + if (err) + return (B_FALSE); + + if (id != ACL_UNDEFINED_ID) { + err = acl_set_qualifier(entry, &id); + } + + return (err ? B_FALSE : B_TRUE); +} + +boolean_t +zfsace_set_entry_type(zfsacl_entry_t _entry, zfsace_entry_type_t _tp) +{ + acl_entry_t entry = BSDACE(_entry); + acl_entry_type_t etype; + int err; + + switch (_tp) { + case ZFSACL_ENTRY_TYPE_ALLOW: + etype = ACL_ENTRY_TYPE_ALLOW; + break; + case ZFSACL_ENTRY_TYPE_DENY: + etype = ACL_ENTRY_TYPE_DENY; + break; + case ZFSACL_ENTRY_TYPE_AUDIT: + etype = ACL_ENTRY_TYPE_AUDIT; + break; + case ZFSACL_ENTRY_TYPE_ALARM: + etype = ACL_ENTRY_TYPE_ALARM; + break; + default: + abort(); + }; + + err = acl_set_entry_type_np(entry, etype); + + return (err ? B_FALSE : B_TRUE); +} + +boolean_t +zfsacl_to_native(zfsacl_t _acl, struct native_acl *pnative) +{ + (void) _acl; + (void) pnative; + errno = EOPNOTSUPP; + return (B_FALSE); +} + +boolean_t +zfsacl_is_trivial(zfsacl_t _acl, boolean_t *_trivialp) +{ + acl_t acl = BSDACL(_acl); + int err, triv; + + err = acl_is_trivial_np(acl, &triv); + if (err) { + return (B_FALSE); + } + + *_trivialp = (triv == 1) ? B_TRUE : B_FALSE; + return (B_TRUE); +} + +char * +zfsacl_to_text(zfsacl_t _acl) +{ + acl_t acl = BSDACL(_acl); + return (acl_to_text_np(acl, NULL, ACL_TEXT_NUMERIC_IDS)); +} diff --git a/lib/libzfsacl/zfsacl/libzfsacl_impl_linux.c b/lib/libzfsacl/zfsacl/libzfsacl_impl_linux.c new file mode 100644 index 000000000000..8c6584912fb1 --- /dev/null +++ b/lib/libzfsacl/zfsacl/libzfsacl_impl_linux.c @@ -0,0 +1,999 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or https://opensource.org/licenses/CDDL-1.0. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2022 Andrew Walker + * All rights reserved. + */ + +#include +#include +#include +#include + + +#define ACL4_MAX_ENTRIES 64 +#define ACL4_XATTR "system.nfs4_acl_xdr" + +/* Non-ACL metadata */ +#define ACL_GET_SZ(aclp) ((size_t)*(aclp)) + +/* NFSv4 ACL metadata */ +#define ACL4_GET_FL(aclp) (aclp) +#define ACL4_GET_CNT(aclp) (ACL4_GET_FL(aclp) + 1) + +/* NFSv4 ACL ENTRY */ +#define ACE4_SZ (sizeof (uint_t) * 5) +#define ACL4_METADATA (sizeof (uint_t) * 2) +#define ACL4SZ_FROM_ACECNT(cnt) (ACL4_METADATA + (cnt * ACE4_SZ)) +#define ACL4_GETENTRY(aclp, idx) \ + (zfsacl_entry_t)((char *)aclp + ACL4SZ_FROM_ACECNT(idx)) +#define ACLBUF_TO_ACES(aclp) ( + +#define ACL4BUF_TO_ACES(aclp) ((struct zfsace4 *)(aclp + 2)) + +static boolean_t +acl_check_brand(zfsacl_t _acl, zfsacl_brand_t expected) +{ + if (_acl->brand != expected) { +#if ZFS_DEBUG + (void) fprintf(stderr, "Incorrect ACL brand"); +#endif + errno = ENOSYS; + return (B_FALSE); + } + return (B_TRUE); +} + +zfsacl_t +zfsacl_init(int _acecnt, zfsacl_brand_t _brand) +{ + size_t naclsz; + zfsacl_t out = NULL; + if (_brand != ZFSACL_BRAND_NFSV4) { + errno = EINVAL; + return (NULL); + } + + out = calloc(1, sizeof (struct zfsacl)); + if (out == NULL) { + return (NULL); + } + + naclsz = ACL4SZ_FROM_ACECNT(_acecnt); + out->aclbuf = calloc(naclsz, sizeof (char)); + if (out->aclbuf == NULL) { + free(out); + return (NULL); + } + out->brand = _brand; + out->aclbuf_size = naclsz; + return (out); +} + +void +zfsacl_free(zfsacl_t *_pacl) +{ + zfsacl_t to_free = *_pacl; + free(to_free->aclbuf); + free(to_free); + *_pacl = NULL; +} + +boolean_t +zfsacl_get_brand(zfsacl_t _acl, zfsacl_brand_t *_brandp) +{ + *_brandp = _acl->brand; + return (B_TRUE); +} + +boolean_t +zfsacl_get_aclflags(zfsacl_t _acl, zfsacl_aclflags_t *_paclflags) +{ + zfsacl_aclflags_t flags; + + if (!acl_check_brand(_acl, ZFSACL_BRAND_NFSV4)) { + return (B_FALSE); + } + + flags = ntohl(*ACL4_GET_FL(_acl->aclbuf)); + *_paclflags = flags; + return (B_TRUE); +} + +boolean_t +zfsacl_set_aclflags(zfsacl_t _acl, zfsacl_aclflags_t _aclflags) +{ + zfsacl_aclflags_t *flags; + + if (!acl_check_brand(_acl, ZFSACL_BRAND_NFSV4)) { + return (B_FALSE); + } + + if (ZFSACL_FLAGS_INVALID(_aclflags)) { +#if ZFS_DEBUG + (void) fprintf(stderr, "Incorrect ACL brand"); +#endif + errno = EINVAL; + return (B_FALSE); + } + + flags = ACL4_GET_FL(_acl->aclbuf); + *flags = htonl(_aclflags); + + return (B_TRUE); +} + +boolean_t +zfsacl_get_acecnt(zfsacl_t _acl, uint_t *pcnt) +{ + uint_t acecnt; + if (!acl_check_brand(_acl, ZFSACL_BRAND_NFSV4)) { + return (B_FALSE); + } + + acecnt = ntohl(*ACL4_GET_CNT(_acl->aclbuf)); + *pcnt = acecnt; + return (B_TRUE); +} + + +static boolean_t +validate_entry_idx(zfsacl_t _acl, int _idx) +{ + uint_t acecnt; + boolean_t ok; + + ok = zfsacl_get_acecnt(_acl, &acecnt); + if (!ok) { + return (B_FALSE); + } + + if ((((uint_t)_idx) + 1) > acecnt) { + errno = E2BIG; + return (B_FALSE); + } + + return (B_TRUE); +} + +/* out will be set to new required size if realloc required */ +static boolean_t +acl_get_new_size(zfsacl_t _acl, uint_t new_count, size_t *out) +{ + size_t current_sz, required_sz; + + if (new_count > ACL4_MAX_ENTRIES) { + errno = E2BIG; + return (B_FALSE); + } + current_sz = _acl->aclbuf_size; + required_sz = ACL4SZ_FROM_ACECNT(new_count); + + if (current_sz >= required_sz) { + *out = 0; + } else { + *out = required_sz; + } + + return (B_TRUE); +} + +boolean_t +zfsacl_create_aclentry(zfsacl_t _acl, int _idx, zfsacl_entry_t *_pentry) +{ + uint_t acecnt; + uint_t *pacecnt; + zfsacl_entry_t entry; + size_t new_size, new_offset, acl_size; + boolean_t ok; + struct zfsace4 *z = ACL4BUF_TO_ACES(_acl->aclbuf); + + ok = zfsacl_get_acecnt(_acl, &acecnt); + if (!ok) { + return (B_FALSE); + } + + if ((_idx != ZFSACL_APPEND_ENTRY) && (((uint_t)_idx) + 1 > acecnt)) { + errno = ERANGE; + return (B_FALSE); + } + + ok = acl_get_new_size(_acl, acecnt + 1, &new_size); + if (!ok) { + return (B_FALSE); + } + + acl_size = _acl->aclbuf_size; + + if (new_size != 0) { + zfsacl_t _tmp = realloc(_acl->aclbuf, new_size); + if (_tmp == NULL) { + errno = ENOMEM; + return (B_FALSE); + } + _acl->aclbuf_size = new_size; + assert(new_size == (acl_size + ACE4_SZ)); + memset(_acl->aclbuf + (new_size - ACE4_SZ), 0, ACE4_SZ); + } + + if (_idx == ZFSACL_APPEND_ENTRY) { + *_pentry = &z[acecnt]; + goto done; + } + + new_offset = ACL4SZ_FROM_ACECNT(_idx); + + /* + * shift back one ace from offset + * to make room for new entry + */ + entry = &z[_idx]; + memmove(entry + 1, entry, acl_size - new_offset - ACE4_SZ); + + /* zero-out new ACE */ + memset(entry, 0, ACE4_SZ); + *_pentry = entry; + +done: + pacecnt = ACL4_GET_CNT(_acl->aclbuf); + *pacecnt = htonl(acecnt + 1); + return (B_TRUE); +} + +#if ZFS_DEBUG +static void +dump_entry(struct zfsace4 *z) +{ + fprintf(stderr, + "0x%08X %p " + "0x%08X %p " + "0x%08X %p " + "0x%08X %p " + "0x%08X %p \n", + z->netlong[0], + &z->netlong[0], + z->netlong[1], + &z->netlong[1], + z->netlong[2], + &z->netlong[2], + z->netlong[3], + &z->netlong[3], + z->netlong[4], + &z->netlong[4]); +} +#endif + +boolean_t +zfsacl_get_aclentry(zfsacl_t _acl, int _idx, zfsacl_entry_t *_pentry) +{ + zfsacl_entry_t entry; + + if (!validate_entry_idx(_acl, _idx)) { + return (B_FALSE); + } + + entry = ACL4_GETENTRY(_acl->aclbuf, _idx); + *_pentry = entry; +#if ZFS_DEBUG + dump_entry(entry); +#endif + return (B_TRUE); +} + +boolean_t +zfsacl_delete_aclentry(zfsacl_t _acl, int _idx) +{ + uint_t acecnt; + uint_t *aclacecnt = NULL; + boolean_t ok; + struct zfsace4 *z = ACL4BUF_TO_ACES(_acl->aclbuf); + size_t orig_sz, after_offset; + + if (!validate_entry_idx(_acl, _idx)) { + return (B_FALSE); + } + + ok = zfsacl_get_acecnt(_acl, &acecnt); + if (!ok) { + return (B_FALSE); + } + + if (acecnt == 1) { + /* ACL without entries is not permitted */ + errno = ERANGE; + return (B_FALSE); + } + + if (((uint_t)_idx) + 1 == acecnt) { + memset(&z[_idx], 0, ACE4_SZ); + } else { + orig_sz = _acl->aclbuf_size; + after_offset = orig_sz - ACL4SZ_FROM_ACECNT(_idx) - ACE4_SZ; + memmove(&z[_idx], &z[_idx + 1], after_offset); + } + + aclacecnt = ACL4_GET_CNT(_acl->aclbuf); + *aclacecnt = htonl(acecnt -1); + return (B_TRUE); +} + +#define ZFSACE_TYPE_OFFSET 0 +#define ZFSACE_FLAGSET_OFFSET 1 +#define ZFSACE_WHOTYPE_OFFSET 2 +#define ZFSACE_PERMSET_OFFSET 3 +#define ZFSACE_WHOID_OFFSET 4 +#define ZFSACE_SPECIAL_ID 0x00000001 +#define HAS_SPECIAL_ID(who) ((who == ZFSACE_SPECIAL_ID) ? B_TRUE : B_FALSE) + +boolean_t +zfsace_get_permset(zfsacl_entry_t _entry, zfsace_permset_t *_pperm) +{ + uint_t *entry = (uint_t *)_entry; + zfsace_permset_t perm; + + perm = ntohl(*(entry + ZFSACE_PERMSET_OFFSET)); + *_pperm = perm; + return (B_TRUE); +} + +boolean_t +zfsace_get_flagset(zfsacl_entry_t _entry, zfsace_flagset_t *_pflags) +{ + uint_t *entry = (uint_t *)_entry; + zfsace_flagset_t flags; + + flags = ntohl(*(entry + ZFSACE_FLAGSET_OFFSET)); + *_pflags = flags; + return (B_TRUE); +} + +boolean_t +zfsace_get_who(zfsacl_entry_t _entry, zfsace_who_t *pwho, zfsace_id_t *_paeid) +{ + struct zfsace4 *entry = (struct zfsace4 *)_entry; + zfsace_who_t whotype; + zfsace_id_t whoid; + zfsace_flagset_t flags; + boolean_t is_special; + + is_special = + HAS_SPECIAL_ID(ntohl(entry->netlong[ZFSACE_WHOTYPE_OFFSET])); + + if (is_special) { + whotype = ntohl(entry->netlong[ZFSACE_WHOID_OFFSET]); + whoid = ZFSACL_UNDEFINED_ID; + } else { + flags = ntohl(entry->netlong[ZFSACE_FLAGSET_OFFSET]); + if (ZFSACE_IS_GROUP(flags)) { + whotype = ZFSACL_GROUP; + } else { + whotype = ZFSACL_USER; + } + whoid = ntohl(entry->netlong[ZFSACE_WHOID_OFFSET]); + } + + *pwho = whotype; + *_paeid = whoid; + return (B_TRUE); +} + +boolean_t +zfsace_get_entry_type(zfsacl_entry_t _entry, zfsace_entry_type_t *_tp) +{ + uint_t *entry = (uint_t *)_entry; + zfsace_entry_type_t entry_type; + + entry_type = ntohl(*(entry + ZFSACE_TYPE_OFFSET)); + *_tp = entry_type; + return (B_TRUE); +} + +boolean_t +zfsace_set_permset(zfsacl_entry_t _entry, zfsace_permset_t _perm) +{ + uint_t *pperm = (uint_t *)_entry + ZFSACE_PERMSET_OFFSET; + + if (ZFSACE_ACCESS_MASK_INVALID(_perm)) { + errno = EINVAL; + return (B_FALSE); + } + + *pperm = htonl(_perm); + return (B_TRUE); +} + +boolean_t +zfsace_set_flagset(zfsacl_entry_t _entry, zfsace_flagset_t _flags) +{ + uint_t *pflags = (uint_t *)_entry + ZFSACE_FLAGSET_OFFSET; + + if (ZFSACE_FLAG_INVALID(_flags)) { + errno = EINVAL; + return (B_FALSE); + } + + *pflags = htonl(_flags); + return (B_TRUE); +} + +boolean_t +zfsace_set_who(zfsacl_entry_t _entry, zfsace_who_t _whotype, zfsace_id_t _whoid) +{ + struct zfsace4 *entry = (struct zfsace4 *)_entry; + uint_t *pspecial = &entry->netlong[ZFSACE_WHOTYPE_OFFSET]; + uint_t *pwhoid = &entry->netlong[ZFSACE_WHOID_OFFSET]; + uint_t special_flag, whoid; + zfsace_flagset_t flags; + + flags = ntohl(entry->netlong[ZFSACE_FLAGSET_OFFSET]); + + switch (_whotype) { + case ZFSACL_USER_OBJ: + case ZFSACL_EVERYONE: + whoid = _whotype; + special_flag = ZFSACE_SPECIAL_ID; + if (ZFSACE_IS_GROUP(flags)) { + zfsace_set_flagset(_entry, + flags & ~ZFSACE_IDENTIFIER_GROUP); + } + break; + case ZFSACL_GROUP_OBJ: + whoid = _whotype; + special_flag = ZFSACE_SPECIAL_ID; + if (!ZFSACE_IS_GROUP(flags)) { + zfsace_set_flagset(_entry, + flags | ZFSACE_IDENTIFIER_GROUP); + } + break; + case ZFSACL_USER: + if (_whoid == ZFSACL_UNDEFINED_ID) { + errno = EINVAL; + return (B_FALSE); + } + whoid = _whoid; + special_flag = 0; + if (ZFSACE_IS_GROUP(flags)) { + zfsace_set_flagset(_entry, + flags & ~ZFSACE_IDENTIFIER_GROUP); + } + break; + case ZFSACL_GROUP: + if (_whoid == ZFSACL_UNDEFINED_ID) { + errno = EINVAL; + return (B_FALSE); + } + whoid = _whoid; + special_flag = 0; + if (!ZFSACE_IS_GROUP(flags)) { + zfsace_set_flagset(_entry, + flags | ZFSACE_IDENTIFIER_GROUP); + } + break; + default: + errno = EINVAL; + return (B_FALSE); + } + + *pspecial = htonl(special_flag); + *pwhoid = htonl(whoid); + return (B_TRUE); +} + +boolean_t +zfsace_set_entry_type(zfsacl_entry_t _entry, zfsace_entry_type_t _tp) +{ + uint_t *ptype = (uint_t *)_entry + ZFSACE_TYPE_OFFSET; + + if (ZFSACE_TYPE_INVALID(_tp)) { + errno = EINVAL; + return (B_FALSE); + } + + *ptype = htonl(_tp); + return (B_TRUE); +} + +#if ZFS_DEBUG +static void +dump_xattr(uint_t *buf, size_t len) +{ + size_t i; + + fprintf(stderr, "off: 0, 0x%08x, ptr: %p | ", ntohl(buf[0]), &buf[0]); + fprintf(stderr, "off: 1, 0x%08x, ptr: %p | ", ntohl(buf[1]), &buf[0]); + + for (i = 2; i < (len / sizeof (uint_t)); i++) { + if (((i -2) % 5) == 0) { + fprintf(stderr, "\n"); + } + fprintf(stderr, "off: %ld, 0x%08x, ptr: %p\n", + i, ntohl(buf[i]), &buf[i]); + } +} +#endif + +zfsacl_t +zfsacl_get_fd(int fd, zfsacl_brand_t _brand) +{ + zfsacl_t out = NULL; + ssize_t res; + + if (_brand != ZFSACL_BRAND_NFSV4) { + errno = EINVAL; + return (NULL); + } + + out = zfsacl_init(ACL4_MAX_ENTRIES, _brand); + if (out == NULL) { + return (NULL); + } + + res = fgetxattr(fd, ACL4_XATTR, out->aclbuf, out->aclbuf_size); + if (res == -1) { + zfsacl_free(&out); + return (NULL); + } +#if ZFS_DEBUG + dump_xattr(out->aclbuf, out->aclbuf_size); +#endif + + return (out); +} + +zfsacl_t +zfsacl_get_file(const char *_path_p, zfsacl_brand_t _brand) +{ + zfsacl_t out = NULL; + ssize_t res; + + if (_brand != ZFSACL_BRAND_NFSV4) { + errno = EINVAL; + return (NULL); + } + + out = zfsacl_init(ACL4_MAX_ENTRIES, _brand); + if (out == NULL) { + return (NULL); + } + + res = getxattr(_path_p, ACL4_XATTR, out->aclbuf, out->aclbuf_size); + if (res == -1) { + zfsacl_free(&out); + return (NULL); + } +#if ZFS_DEBUG + dump_xattr(out->aclbuf, out->aclbuf_size); +#endif + + return (out); +} + +zfsacl_t +zfsacl_get_link(const char *_path_p, zfsacl_brand_t _brand) +{ + zfsacl_t out = NULL; + ssize_t res; + + if (_brand != ZFSACL_BRAND_NFSV4) { + errno = EINVAL; + return (NULL); + } + + out = zfsacl_init(ACL4_MAX_ENTRIES, _brand); + if (out == NULL) { + return (NULL); + } + + res = lgetxattr(_path_p, ACL4_XATTR, out->aclbuf, out->aclbuf_size); + if (res == -1) { + zfsacl_free(&out); + return (NULL); + } + +#if ZFS_DEBUG + dump_xattr(out->aclbuf, out->aclbuf_size); +#endif + return (out); +} + +static boolean_t +xatbuf_from_acl(zfsacl_t acl, char **pbuf, size_t *bufsz) +{ + uint_t acecnt; + size_t calculated_acl_sz; + boolean_t ok; + + ok = zfsacl_get_acecnt(acl, &acecnt); + if (!ok) { + return (B_FALSE); + } + + if (acecnt == 0) { + errno = ENODATA; + } else if (acecnt > ACL4_MAX_ENTRIES) { + errno = ERANGE; + return (B_FALSE); + } + + calculated_acl_sz = ACL4SZ_FROM_ACECNT(acecnt); + assert(calculated_acl_sz <= acl->aclbuf_size); + + *pbuf = (char *)acl->aclbuf; + + *bufsz = calculated_acl_sz; + return (B_TRUE); +} + +boolean_t +zfsacl_set_fd(int _fd, zfsacl_t _acl) +{ + int err; + boolean_t ok; + char *buf = NULL; + size_t bufsz = 0; + + ok = xatbuf_from_acl(_acl, &buf, &bufsz); + if (!ok) { + return (B_FALSE); + } + + err = fsetxattr(_fd, ACL4_XATTR, buf, bufsz, 0); + if (err) { + return (B_FALSE); + } + return (B_TRUE); +} + +boolean_t +zfsacl_set_file(const char *_path_p, zfsacl_t _acl) +{ + int err; + boolean_t ok; + char *buf = NULL; + size_t bufsz = 0; + + ok = xatbuf_from_acl(_acl, &buf, &bufsz); + if (!ok) { + return (B_FALSE); + } + + err = setxattr(_path_p, ACL4_XATTR, buf, bufsz, 0); + if (err) { + return (B_FALSE); + } + return (B_TRUE); +} + +boolean_t +zfsacl_set_link(const char *_path_p, zfsacl_t _acl) +{ + int err; + boolean_t ok; + char *buf = NULL; + size_t bufsz = 0; + + ok = xatbuf_from_acl(_acl, &buf, &bufsz); + if (!ok) { + return (B_FALSE); + } + + err = lsetxattr(_path_p, ACL4_XATTR, buf, bufsz, 0); + if (err) { + return (B_FALSE); + } + return (B_TRUE); +} + +boolean_t +zfsacl_to_native(zfsacl_t _acl, struct native_acl *pnative) +{ + char *to_copy = NULL; + char *out_buf = NULL; + size_t bufsz; + boolean_t ok; + + if (pnative == NULL) { + errno = ENOMEM; + return (B_FALSE); + } + + ok = xatbuf_from_acl(_acl, &to_copy, &bufsz); + if (!ok) { + return (B_FALSE); + } + + out_buf = calloc(bufsz, sizeof (char)); + if (out_buf == NULL) { + errno = ENOMEM; + return (B_FALSE); + } + memcpy(out_buf, to_copy, bufsz); + pnative->data = out_buf; + pnative->datalen = bufsz; + pnative->brand = _acl->brand; + return (B_TRUE); +} + +boolean_t +zfsacl_is_trivial(zfsacl_t _acl, boolean_t *trivialp) +{ + (void) _acl; + (void) trivialp; + errno = EOPNOTSUPP; + return (B_FALSE); +} + +#define MAX_ENTRY_LENGTH 512 + +aceperms2name_t _aceperm2name[] = { + { ZFSACE_READ_DATA, "READ_DATA", 'r' }, + { ZFSACE_LIST_DIRECTORY, "LIST_DIRECTORY", '\0' }, + { ZFSACE_WRITE_DATA, "WRITE_DATA", 'w' }, + { ZFSACE_ADD_FILE, "ADD_FILE", '\0' }, + { ZFSACE_APPEND_DATA, "APPEND_DATA", 'p' }, + { ZFSACE_DELETE, "DELETE", 'd' }, + { ZFSACE_DELETE_CHILD, "DELETE_CHILD", 'D' }, + { ZFSACE_ADD_SUBDIRECTORY, "ADD_SUBDIRECTORY", '\0' }, + { ZFSACE_READ_ATTRIBUTES, "READ_ATTRIBUTES", 'a' }, + { ZFSACE_WRITE_ATTRIBUTES, "WRITE_ATTRIBUTES", 'A' }, + { ZFSACE_READ_NAMED_ATTRS, "READ_NAMED_ATTRS", 'R' }, + { ZFSACE_WRITE_NAMED_ATTRS, "WRITE_NAMED_ATTRS", 'W' }, + { ZFSACE_READ_ACL, "READ_ACL", 'c' }, + { ZFSACE_WRITE_ACL, "WRITE_ACL", 'C' }, + { ZFSACE_WRITE_OWNER, "WRITE_OWNER", 'o' }, + { ZFSACE_SYNCHRONIZE, "SYNCHRONIZE", 's' }, +}; + +static boolean_t +format_perms(char *str, const zfsacl_entry_t entry, size_t *off) +{ + int i, cnt = 0; + zfsace_permset_t p; + + if (!zfsace_get_permset(entry, &p)) { + return (B_FALSE); + } + + for (i = 0; i < ARRAY_SIZE(_aceperm2name); i++) { + char to_set; + + if (_aceperm2name[i].letter == '\0') { + continue; + } + if (p & _aceperm2name[i].perm) { + to_set = _aceperm2name[i].letter; + } else { + to_set = '-'; + } + str[cnt] = to_set; + cnt++; + } + + *off += cnt; + return (B_TRUE); +} + +aceflags2name_t aceflag2name[] = { + { ZFSACE_FILE_INHERIT, "FILE_INHERIT", 'f' }, + { ZFSACE_DIRECTORY_INHERIT, "DIRECTORY_INHERIT", 'd' }, + { ZFSACE_INHERIT_ONLY, "INHERIT_ONLY", 'i' }, + { ZFSACE_NO_PROPAGATE_INHERIT, "NO_PROPAGATE_INHERIT", 'n' }, + { ZFSACE_INHERITED_ACE, "INHERITED", 'I' }, +}; + +static boolean_t +format_flags(char *str, const zfsacl_entry_t entry, size_t *off) +{ + int i, cnt = 0; + zfsace_flagset_t flag; + + if (!zfsace_get_flagset(entry, &flag)) { + return (B_FALSE); + } + + for (i = 0; i < ARRAY_SIZE(aceflag2name); i++) { + char to_set; + + if (aceflag2name[i].letter == '\0') { + continue; + } + if (flag & aceflag2name[i].flag) { + to_set = aceflag2name[i].letter; + } else { + to_set = '-'; + } + str[cnt] = to_set; + cnt++; + } + + *off += cnt; + return (B_TRUE); +} + +static boolean_t +format_who(char *str, size_t sz, const zfsacl_entry_t _entry, size_t *off) +{ + uid_t id; + zfsace_who_t who; + int cnt = 0; + + if (!zfsace_get_who(_entry, &who, &id)) { + return (B_FALSE); + } + + switch (who) { + case ZFSACL_USER_OBJ: + cnt = snprintf(str, sz, "owner@"); + break; + case ZFSACL_GROUP_OBJ: + cnt = snprintf(str, sz, "group@"); + break; + case ZFSACL_EVERYONE: + cnt = snprintf(str, sz, "everyone@"); + break; + case ZFSACL_USER: + cnt = snprintf(str, sz, "user:%d", id); + break; + case ZFSACL_GROUP: + cnt = snprintf(str, sz, "group:%d", id); + break; + default: + errno = EINVAL; + return (B_FALSE); + } + + if (cnt == -1) { + return (B_FALSE); + } + + *off += cnt; + return (B_TRUE); +} + +static boolean_t +format_entry_type(char *str, size_t sz, const zfsacl_entry_t _entry, + size_t *off) +{ + zfsace_entry_type_t entry_type; + int cnt = 0; + + if (!zfsace_get_entry_type(_entry, &entry_type)) { + return (B_FALSE); + } + + switch (entry_type) { + case ZFSACL_ENTRY_TYPE_ALLOW: + cnt = snprintf(str, sz, "allow"); + break; + case ZFSACL_ENTRY_TYPE_DENY: + cnt = snprintf(str, sz, "deny"); + break; + case ZFSACL_ENTRY_TYPE_AUDIT: + cnt = snprintf(str, sz, "audit"); + break; + case ZFSACL_ENTRY_TYPE_ALARM: + cnt = snprintf(str, sz, "alarm"); + break; + default: + errno = EINVAL; + return (B_FALSE); + } + + if (cnt == -1) { + return (B_FALSE); + } + + *off += cnt; + return (B_TRUE); +} + +static boolean_t +add_format_separator(char *str, size_t sz, size_t *off) +{ + int cnt; + + cnt = snprintf(str, sz, ":"); + if (cnt == -1) + return (B_FALSE); + + *off += cnt; + return (B_TRUE); +} + +static size_t +format_entry(char *str, size_t sz, const zfsacl_entry_t _entry) +{ + size_t off = 0; + size_t slen = 0; + size_t tocopy = 0; + char buf[MAX_ENTRY_LENGTH + 1] = { 0 }; + + if (!format_who(buf, sizeof (buf), _entry, &off)) + return (-1); + + if (!add_format_separator(buf +off, sizeof (buf) - off, &off)) + return (-1); + + if (!format_perms(buf + off, _entry, &off)) + return (-1); + + if (!add_format_separator(buf +off, sizeof (buf) - off, &off)) + return (-1); + + if (!format_flags(buf + off, _entry, &off)) + return (-1); + + if (!add_format_separator(buf +off, sizeof (buf) - off, &off)) + return (-1); + + if (!format_entry_type(buf + off, sizeof (buf) - off, _entry, &off)) + return (-1); + + buf[off] = '\n'; + slen = strlen(buf); + if (slen >= sz) + tocopy = sz - 1; + else + tocopy = slen; + memcpy(str, buf, tocopy); + str[tocopy] = '\0'; + return (tocopy); +} + +char * +zfsacl_to_text(zfsacl_t _acl) +{ + uint_t acecnt, i; + char *str = NULL; + size_t off = 0, bufsz; + + if (!zfsacl_get_acecnt(_acl, &acecnt)) { + return (NULL); + } + + str = calloc(acecnt, MAX_ENTRY_LENGTH); + if (str == NULL) { + return (NULL); + } + + bufsz = acecnt * MAX_ENTRY_LENGTH; + + for (i = 0; i < acecnt; i++) { + zfsacl_entry_t entry; + size_t written; + + if (!zfsacl_get_aclentry(_acl, i, &entry)) { + free(str); + return (NULL); + } + + written = format_entry(str + off, bufsz - off, entry); + if (written == (size_t)-1) { + free(str); + return (NULL); + } + + off += written; + } + + return (str); +} diff --git a/rpm/generic/zfs.spec.in b/rpm/generic/zfs.spec.in index 2e89abd0edfd..c9c231b7d968 100644 --- a/rpm/generic/zfs.spec.in +++ b/rpm/generic/zfs.spec.in @@ -346,6 +346,22 @@ This package contains the pam_zfs_key PAM module, which provides support for unlocking datasets on user login. %endif +%package -n python%{__python_pkg_version}-libzfsacl +Summary: Python bindings for NFSv41 ACLs +Group: System Environment/Kernel +Requires: python%{__python_pkg_version} + +%if 0%{?rhel}%{?centos}%{?fedora}%{?suse_version}%{?openEuler} +%if 0%{?centos} == 7 +BuildRequires: python36-setuptools +%else +BuildRequires: python%{__python_pkg_version}-setuptools +%endif +%endif + +%description -n python%{__python_pkg_version}-libzfsacl +This package contains python library for accessing NFSv41 ACLS. + %prep %if %{with debug} %define debug --enable-debug @@ -543,7 +559,9 @@ systemctl --system daemon-reload >/dev/null || true %{_libdir}/libuutil.so.* %files -n libzfs5 -%{_libdir}/libzfs*.so.* +%{_libdir}/libzfs.so.* +%{_libdir}/libzfsbootenv.so.* +%{_libdir}/libzfs_core.so.* %files -n libzfs5-devel %{_pkgconfigdir}/libzfs.pc @@ -587,3 +605,9 @@ systemctl --system daemon-reload >/dev/null || true %{_libdir}/security/* %{_datadir}/pam-configs/* %endif + +%files -n python%{__python_pkg_version}-libzfsacl +%{__python_sitelib}/libzfsacl-*/* +%{__python_sitelib}/libzfsacl.cpython*.so +%{_libdir}/libzfsacl.so.* +%{_libdir}/libsunacl.so.*