- nvlist_t *who_nvp;
- nvlist_t *perms_nvp = NULL;
- nvlist_t *sets_nvp = NULL;
- char errbuf[1024];
- char *who_tok, *perm;
- int error;
-
- *nvp = NULL;
-
- if (perms) {
- if ((error = nvlist_alloc(&perms_nvp,
- NV_UNIQUE_NAME, 0)) != 0) {
- return (1);
- }
- if ((error = nvlist_alloc(&sets_nvp,
- NV_UNIQUE_NAME, 0)) != 0) {
- nvlist_free(perms_nvp);
- return (1);
- }
- }
-
- if ((error = nvlist_alloc(&who_nvp, NV_UNIQUE_NAME, 0)) != 0) {
- if (perms_nvp)
- nvlist_free(perms_nvp);
- if (sets_nvp)
- nvlist_free(sets_nvp);
- return (1);
- }
-
- if (who_type == ZFS_DELEG_NAMED_SET) {
- namecheck_err_t why;
- char what;
-
- if ((error = permset_namecheck(whostr, &why, &what)) != 0) {
- nvlist_free(who_nvp);
- if (perms_nvp)
- nvlist_free(perms_nvp);
- if (sets_nvp)
- nvlist_free(sets_nvp);
-
- switch (why) {
- case NAME_ERR_NO_AT:
- zfs_error_aux(zhp->zfs_hdl,
- dgettext(TEXT_DOMAIN,
- "set definition must begin with an '@' "
- "character"));
- }
- return (zfs_error(zhp->zfs_hdl,
- EZFS_BADPERMSET, whostr));
- }
- }
-
- /*
- * Build up nvlist(s) of permissions. Two nvlists are maintained.
- * The first nvlist perms_nvp will have normal permissions and the
- * other sets_nvp will have only permssion set names in it.
- */
- for (perm = strtok(perms, ","); perm; perm = strtok(NULL, ",")) {
- const char *perm_canonical = zfs_deleg_canonicalize_perm(perm);
-
- if (perm_canonical) {
- verify(nvlist_add_boolean(perms_nvp,
- perm_canonical) == 0);
- } else if (perm[0] == '@') {
- verify(nvlist_add_boolean(sets_nvp, perm) == 0);
- } else {
- nvlist_free(who_nvp);
- nvlist_free(perms_nvp);
- nvlist_free(sets_nvp);
- return (zfs_error(zhp->zfs_hdl, EZFS_BADPERM, perm));
- }
- }
-
- if (whostr && who_type != ZFS_DELEG_CREATE) {
- who_tok = strtok(whostr, ",");
- if (who_tok == NULL) {
- nvlist_free(who_nvp);
- if (perms_nvp)
- nvlist_free(perms_nvp);
- if (sets_nvp)
- nvlist_free(sets_nvp);
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "Who string is NULL"),
- whostr);
- return (zfs_error(zhp->zfs_hdl, EZFS_BADWHO, errbuf));
- }
- }
-
- /*
- * Now create the nvlist(s)
- */
- do {
- uint64_t who_id;
-
- error = zfs_get_perm_who(who_tok, &who_type,
- &who_id);
- if (error) {
- nvlist_free(who_nvp);
- if (perms_nvp)
- nvlist_free(perms_nvp);
- if (sets_nvp)
- nvlist_free(sets_nvp);
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN,
- "Unable to determine uid/gid for "
- "%s "), who_tok);
- return (zfs_error(zhp->zfs_hdl, EZFS_BADWHO, errbuf));
- }
-
- /*
- * add entries for both local and descendent when required
- */
- zfs_perms_add_who_nvlist(who_nvp, who_id, who_tok,
- perms_nvp, sets_nvp, who_type, inherit);
-
- } while (who_tok = strtok(NULL, ","));
- *nvp = who_nvp;
- return (0);
-}
-
-static int
-zfs_perm_set_common(zfs_handle_t *zhp, nvlist_t *nvp, boolean_t unset)
-{
- zfs_cmd_t zc = { 0 };
- int error;
- char errbuf[1024];
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "Cannot update 'allows' for '%s'"),
- zhp->zfs_name);
-
- if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, nvp))
- return (-1);
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- zc.zc_perm_action = unset;
-
- error = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SET_FSACL, &zc);
- if (error && errno == ENOTSUP) {
- (void) snprintf(errbuf, sizeof (errbuf),
- gettext("Pool must be upgraded to use 'allow/unallow'"));
- zcmd_free_nvlists(&zc);
- return (zfs_error(zhp->zfs_hdl, EZFS_BADVERSION, errbuf));
- } else if (error) {
- return (zfs_standard_error(zhp->zfs_hdl, errno, errbuf));
- }
- zcmd_free_nvlists(&zc);
-
- return (error);
-}
-
-int
-zfs_perm_set(zfs_handle_t *zhp, nvlist_t *nvp)
-{
- return (zfs_perm_set_common(zhp, nvp, B_FALSE));
-}
-
-int
-zfs_perm_remove(zfs_handle_t *zhp, nvlist_t *perms)
-{
- return (zfs_perm_set_common(zhp, perms, B_TRUE));
-}
-
-static int
-perm_compare(const void *arg1, const void *arg2)
-{
- const zfs_perm_node_t *node1 = arg1;
- const zfs_perm_node_t *node2 = arg2;
- int ret;
-
- ret = strcmp(node1->z_pname, node2->z_pname);
-
- if (ret > 0)
- return (1);
- if (ret < 0)
- return (-1);
- else
- return (0);
-}
-
-static void
-zfs_destroy_perm_tree(avl_tree_t *tree)
-{
- zfs_perm_node_t *permnode;
- void *cookie = NULL;
-
- while ((permnode = avl_destroy_nodes(tree, &cookie)) != NULL)
- free(permnode);
- avl_destroy(tree);
-}
-
-static void
-zfs_destroy_tree(avl_tree_t *tree)
-{
- zfs_allow_node_t *allownode;
- void *cookie = NULL;
-
- while ((allownode = avl_destroy_nodes(tree, &cookie)) != NULL) {
- zfs_destroy_perm_tree(&allownode->z_localdescend);
- zfs_destroy_perm_tree(&allownode->z_local);
- zfs_destroy_perm_tree(&allownode->z_descend);
- free(allownode);
- }
- avl_destroy(tree);
-}
-
-void
-zfs_free_allows(zfs_allow_t *allow)
-{
- zfs_allow_t *allownext;
- zfs_allow_t *freeallow;
-
- allownext = allow;
- while (allownext) {
- zfs_destroy_tree(&allownext->z_sets);
- zfs_destroy_tree(&allownext->z_crperms);
- zfs_destroy_tree(&allownext->z_user);
- zfs_destroy_tree(&allownext->z_group);
- zfs_destroy_tree(&allownext->z_everyone);
- freeallow = allownext;
- allownext = allownext->z_next;
- free(freeallow);
- }
-}
-
-static zfs_allow_t *
-zfs_alloc_perm_tree(zfs_handle_t *zhp, zfs_allow_t *prev, char *setpoint)
-{
- zfs_allow_t *ptree;
-
- if ((ptree = zfs_alloc(zhp->zfs_hdl,
- sizeof (zfs_allow_t))) == NULL) {
- return (NULL);
- }
-
- (void) strlcpy(ptree->z_setpoint, setpoint, sizeof (ptree->z_setpoint));
- avl_create(&ptree->z_sets,
- perm_compare, sizeof (zfs_allow_node_t),
- offsetof(zfs_allow_node_t, z_node));
- avl_create(&ptree->z_crperms,
- perm_compare, sizeof (zfs_allow_node_t),
- offsetof(zfs_allow_node_t, z_node));
- avl_create(&ptree->z_user,
- perm_compare, sizeof (zfs_allow_node_t),
- offsetof(zfs_allow_node_t, z_node));
- avl_create(&ptree->z_group,
- perm_compare, sizeof (zfs_allow_node_t),
- offsetof(zfs_allow_node_t, z_node));
- avl_create(&ptree->z_everyone,
- perm_compare, sizeof (zfs_allow_node_t),
- offsetof(zfs_allow_node_t, z_node));
-
- if (prev)
- prev->z_next = ptree;
- ptree->z_next = NULL;
- return (ptree);
-}
-
-/*
- * Add permissions to the appropriate AVL permission tree.
- * The appropriate tree may not be the requested tree.
- * For example if ld indicates a local permission, but
- * same permission also exists as a descendent permission
- * then the permission will be removed from the descendent
- * tree and add the the local+descendent tree.
- */
-static int
-zfs_coalesce_perm(zfs_handle_t *zhp, zfs_allow_node_t *allownode,
- char *perm, char ld)
-{
- zfs_perm_node_t pnode, *permnode, *permnode2;
- zfs_perm_node_t *newnode;
- avl_index_t where, where2;
- avl_tree_t *tree, *altree;
-
- (void) strlcpy(pnode.z_pname, perm, sizeof (pnode.z_pname));
-
- if (ld == ZFS_DELEG_NA) {
- tree = &allownode->z_localdescend;
- altree = &allownode->z_descend;
- } else if (ld == ZFS_DELEG_LOCAL) {
- tree = &allownode->z_local;
- altree = &allownode->z_descend;
- } else {
- tree = &allownode->z_descend;
- altree = &allownode->z_local;
- }
- permnode = avl_find(tree, &pnode, &where);
- permnode2 = avl_find(altree, &pnode, &where2);
-
- if (permnode2) {
- avl_remove(altree, permnode2);
- free(permnode2);
- if (permnode == NULL) {
- tree = &allownode->z_localdescend;
- }
- }
-
- /*
- * Now insert new permission in either requested location
- * local/descendent or into ld when perm will exist in both.
- */
- if (permnode == NULL) {
- if ((newnode = zfs_alloc(zhp->zfs_hdl,
- sizeof (zfs_perm_node_t))) == NULL) {
- return (-1);
- }
- *newnode = pnode;
- avl_add(tree, newnode);
- }
- return (0);
-}
-
-/*
- * Uggh, this is going to be a bit complicated.
- * we have an nvlist coming out of the kernel that
- * will indicate where the permission is set and then
- * it will contain allow of the various "who's", and what
- * their permissions are. To further complicate this
- * we will then have to coalesce the local,descendent
- * and local+descendent permissions where appropriate.
- * The kernel only knows about a permission as being local
- * or descendent, but not both.
- *
- * In order to make this easier for zfs_main to deal with
- * a series of AVL trees will be used to maintain
- * all of this, primarily for sorting purposes as well
- * as the ability to quickly locate a specific entry.
- *
- * What we end up with are tree's for sets, create perms,
- * user, groups and everyone. With each of those trees
- * we have subtrees for local, descendent and local+descendent
- * permissions.
- */
-int
-zfs_perm_get(zfs_handle_t *zhp, zfs_allow_t **zfs_perms)
-{
- zfs_cmd_t zc = { 0 };
- int error;
- nvlist_t *nvlist;
- nvlist_t *permnv, *sourcenv;
- nvpair_t *who_pair, *source_pair;
- nvpair_t *perm_pair;
- char errbuf[1024];
- zfs_allow_t *zallowp, *newallowp;
- char ld;
- char *nvpname;
- uid_t uid;
- gid_t gid;
- avl_tree_t *tree;
- avl_index_t where;
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
- if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
- return (-1);
-
- while (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) != 0) {
- if (errno == ENOMEM) {
- if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, &zc) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- } else if (errno == ENOTSUP) {
- zcmd_free_nvlists(&zc);
- (void) snprintf(errbuf, sizeof (errbuf),
- gettext("Pool must be upgraded to use 'allow'"));
- return (zfs_error(zhp->zfs_hdl,
- EZFS_BADVERSION, errbuf));
- } else {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- }
-
- if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &nvlist) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
-
- zcmd_free_nvlists(&zc);
-
- source_pair = nvlist_next_nvpair(nvlist, NULL);