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.*