From f2e05f082a6bdc0070f17cbeb5df2630f630cb54 Mon Sep 17 00:00:00 2001 From: Umer Saleem Date: Tue, 9 Jan 2024 13:24:46 +0500 Subject: [PATCH] Updates to libs Signed-off-by: Umer Saleem --- contrib/debian/control | 5 +- .../debian/openzfs-python3-libzfsacl.install | 2 + include/Makefile.am | 4 +- {lib/libzfsacl => include}/sunacl.h | 2 +- lib/libzfsacl/libzfsacl.h => include/zfsacl.h | 6 +- lib/Makefile.am | 2 + lib/libzfsacl/Makefile.am | 7 +- lib/libzfsacl/libpyzfsacl.c | 3 +- lib/libzfsacl/libzfsacl_impl_linux.c | 2 +- lib/libzfsacl/setup.py.in | 10 +- lib/libzfsacl/sunacl/Makefile.am | 22 + lib/libzfsacl/{ => sunacl}/libsunacl.c | 8 +- lib/libzfsacl/zfsacl/Makefile.am | 20 + .../{ => zfsacl}/libzfsacl_impl_freebsd.c | 2 +- lib/libzfsacl/zfsacl/libzfsacl_impl_linux.c | 974 ++++++++++++++++++ 15 files changed, 1041 insertions(+), 28 deletions(-) rename {lib/libzfsacl => include}/sunacl.h (98%) rename lib/libzfsacl/libzfsacl.h => include/zfsacl.h (98%) create mode 100644 lib/libzfsacl/sunacl/Makefile.am rename lib/libzfsacl/{ => sunacl}/libsunacl.c (99%) create mode 100644 lib/libzfsacl/zfsacl/Makefile.am rename lib/libzfsacl/{ => zfsacl}/libzfsacl_impl_freebsd.c (99%) create mode 100644 lib/libzfsacl/zfsacl/libzfsacl_impl_linux.c diff --git a/contrib/debian/control b/contrib/debian/control index f625987b3a23..63033c19ef54 100644 --- a/contrib/debian/control +++ b/contrib/debian/control @@ -259,10 +259,9 @@ Depends: openzfs-libnvpair3 (= ${binary:Version}), openzfs-libuutil3 (= ${binary:Version}), openzfs-libzfs4 (= ${binary:Version}), openzfs-libzpool5 (= ${binary:Version}), - openzfs-python3-libzfsacl (= ${binary:Version}) + openzfs-python3-libzfsacl (= ${binary:Version}), python3, - ${misc:Depends}, - ${shlibs:Depends} + ${misc:Depends} Recommends: lsb-base, openzfs-zfs-modules | openzfs-zfs-dkms, openzfs-zfs-zed Breaks: openrc, spl (<< 0.7.9-2), diff --git a/contrib/debian/openzfs-python3-libzfsacl.install b/contrib/debian/openzfs-python3-libzfsacl.install index c45bd772e54d..e27a98776cfd 100644 --- a/contrib/debian/openzfs-python3-libzfsacl.install +++ b/contrib/debian/openzfs-python3-libzfsacl.install @@ -1,2 +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/include/Makefile.am b/include/Makefile.am index 5f38f6ac6ddb..41aee4e0ff84 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -190,7 +190,9 @@ USER_H = \ libzfs_core.h \ libzfsbootenv.h \ libzutil.h \ - thread_pool.h + sunacl.h \ + thread_pool.h \ + zfsacl.h if CONFIG_USER diff --git a/lib/libzfsacl/sunacl.h b/include/sunacl.h similarity index 98% rename from lib/libzfsacl/sunacl.h rename to include/sunacl.h index 85af6714c0a8..7cc6a4bb74a9 100644 --- a/lib/libzfsacl/sunacl.h +++ b/include/sunacl.h @@ -28,7 +28,7 @@ */ #ifndef SUNACL_H -#define SUNACL_H +#define SUNACL_H extern __attribute__((visibility("default"))) #include /* uid_t */ diff --git a/lib/libzfsacl/libzfsacl.h b/include/zfsacl.h similarity index 98% rename from lib/libzfsacl/libzfsacl.h rename to include/zfsacl.h index c7ff7837eaf6..12495cdc1c1b 100644 --- a/lib/libzfsacl/libzfsacl.h +++ b/include/zfsacl.h @@ -1,5 +1,5 @@ #ifndef __ZFSACL_H__ -#define __ZFSACL_H__ +#define __ZFSACL_H__ extern __attribute__((visibility("default"))) #include #include @@ -7,8 +7,9 @@ #include #include #include +#include -typedef enum { B_FALSE, B_TRUE } boolean_t; +#define zfsace4 zfsacl_entry typedef unsigned int uint_t; /* @@ -263,7 +264,6 @@ 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() diff --git a/lib/Makefile.am b/lib/Makefile.am index 31f00c43915c..c7090e192438 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -64,6 +64,7 @@ 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 @@ -71,6 +72,7 @@ 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/Makefile.am b/lib/libzfsacl/Makefile.am index 194326e74166..2d63efc27a52 100644 --- a/lib/libzfsacl/Makefile.am +++ b/lib/libzfsacl/Makefile.am @@ -1,10 +1,5 @@ dist_noinst_DATA += \ - %D%/sunacl.h \ - %D%/libzfsacl.h \ - %D%/libpyzfsacl.c \ - %D%/libsunacl.c \ - %D%/libzfsacl_impl_freebsd.c \ - %D%/libzfsacl_impl_linux.c + %D%/libpyzfsacl.c SUBSTFILES += %D%/setup.py diff --git a/lib/libzfsacl/libpyzfsacl.c b/lib/libzfsacl/libpyzfsacl.c index 4c5fab4756cc..22d278ddc192 100644 --- a/lib/libzfsacl/libpyzfsacl.c +++ b/lib/libzfsacl/libpyzfsacl.c @@ -1,6 +1,7 @@ +#include +#include #include -#include "libzfsacl.h" #define Py_TPFLAGS_HAVE_ITER 0 diff --git a/lib/libzfsacl/libzfsacl_impl_linux.c b/lib/libzfsacl/libzfsacl_impl_linux.c index e8492eb59711..d668291026c6 100644 --- a/lib/libzfsacl/libzfsacl_impl_linux.c +++ b/lib/libzfsacl/libzfsacl_impl_linux.c @@ -796,7 +796,7 @@ format_flags(char *str, size_t sz, const zfsacl_entry_t entry, size_t *off) to_set = '-'; } str[cnt] = to_set; - cnt += rv; + cnt++; } *off += cnt; diff --git a/lib/libzfsacl/setup.py.in b/lib/libzfsacl/setup.py.in index 41265db02552..e02283716617 100644 --- a/lib/libzfsacl/setup.py.in +++ b/lib/libzfsacl/setup.py.in @@ -1,17 +1,13 @@ from setuptools import setup, Extension, find_packages import os -import sys srcdir = '@abs_top_srcdir@/lib/libzfsacl' topsrcdir = '@abs_top_srcdir@' -relpath_topsrcdir = os.path.relpath(topsrcdir) -if sys.platform.startswith('linux'): - src = ['libzfsacl_impl_linux.c', 'libpyzfsacl.c', 'libsunacl.c'] -else: - src = ['libzfsacl_impl_freebsd.c', 'libpyzfsacl.c'] +incdir = [topsrcdir, topsrcdir + '/include', topsrcdir + '/lib/libspl/include' ] +src = ['libpyzfsacl.c'] -libzfsacl_mod = Extension('libzfsacl', include_dirs=[relpath_topsrcdir], +libzfsacl_mod = Extension('libzfsacl', include_dirs=incdir, sources=src) setup( 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/libsunacl.c b/lib/libzfsacl/sunacl/libsunacl.c similarity index 99% rename from lib/libzfsacl/libsunacl.c rename to lib/libzfsacl/sunacl/libsunacl.c index 002a7e604818..eb5eda350337 100644 --- a/lib/libzfsacl/libsunacl.c +++ b/lib/libzfsacl/sunacl/libsunacl.c @@ -27,8 +27,8 @@ #include #include -#include "libzfsacl.h" -#include "sunacl.h" +#include +#include #include @@ -36,7 +36,7 @@ #define ACE_SETACL 5 #define ACE_GETACLCNT 6 -int +static int acl_from_aces(zfsacl_t aclp, const ace_t *aces, int nentries) { int i; @@ -126,7 +126,7 @@ acl_from_aces(zfsacl_t aclp, const ace_t *aces, int nentries) return (0); } -int +static int aces_from_acl(ace_t *aces, int *nentries, zfsacl_t aclp) { int i; diff --git a/lib/libzfsacl/zfsacl/Makefile.am b/lib/libzfsacl/zfsacl/Makefile.am new file mode 100644 index 000000000000..724c0d655f23 --- /dev/null +++ b/lib/libzfsacl/zfsacl/Makefile.am @@ -0,0 +1,20 @@ +libzfsacl_la_CFLAGS = $(AM_CFLAGS) $(LIBRARY_CFLAGS) + +lib_LTLIBRARIES += libzfsacl.la +CPPCHECKTARGETS += libzfsacl.la + +libzfsacl_la_CPPFLAGS = $(AM_CPPFLAGS) + +dist_libzfsacl_la_SOURCES = \ + %D%/libzfsacl_impl_linux.c + +libzfsacl_la_LIBADD = \ + libzfs.la + +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/libzfsacl_impl_freebsd.c b/lib/libzfsacl/zfsacl/libzfsacl_impl_freebsd.c similarity index 99% rename from lib/libzfsacl/libzfsacl_impl_freebsd.c rename to lib/libzfsacl/zfsacl/libzfsacl_impl_freebsd.c index 11c789a43b46..b0ff453da7af 100644 --- a/lib/libzfsacl/libzfsacl_impl_freebsd.c +++ b/lib/libzfsacl/zfsacl/libzfsacl_impl_freebsd.c @@ -1,4 +1,4 @@ -#include "libzfsacl.h" +#include "zfsacl.h" #include #define BSDACE(zfsace) ((acl_entry_t)zfsace) diff --git a/lib/libzfsacl/zfsacl/libzfsacl_impl_linux.c b/lib/libzfsacl/zfsacl/libzfsacl_impl_linux.c new file mode 100644 index 000000000000..15aceba963cc --- /dev/null +++ b/lib/libzfsacl/zfsacl/libzfsacl_impl_linux.c @@ -0,0 +1,974 @@ +#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++) { + int rv; + 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); +}