int dsl_deleg_set(const char *ddname, nvlist_t *nvp, boolean_t unset) { dsl_dir_t *dd; int error; nvpair_t *whopair = NULL; int blocks_modified = 0; error = dsl_dir_open(ddname, FTAG, &dd, NULL); if (error) return (error); if (spa_version(dmu_objset_spa(dd->dd_pool->dp_meta_objset)) < SPA_VERSION_DELEGATED_PERMS) { dsl_dir_close(dd, FTAG); return (ENOTSUP); } while (whopair = nvlist_next_nvpair(nvp, whopair)) blocks_modified++; error = dsl_sync_task_do(dd->dd_pool, NULL, unset ? dsl_deleg_unset_sync : dsl_deleg_set_sync, dd, nvp, blocks_modified); dsl_dir_close(dd, FTAG); return (error); }
int dmu_objset_create(const char *name, dmu_objset_type_t type, objset_t *clone_parent, uint64_t flags, void (*func)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx), void *arg) { dsl_dir_t *pdd; const char *tail; int err = 0; struct oscarg oa = { 0 }; ASSERT(strchr(name, '@') == NULL); err = dsl_dir_open(name, FTAG, &pdd, &tail); if (err) return (err); if (tail == NULL) { dsl_dir_close(pdd, FTAG); return (EEXIST); } dprintf("name=%s\n", name); oa.userfunc = func; oa.userarg = arg; oa.lastname = tail; oa.type = type; oa.flags = flags; if (clone_parent != NULL) { /* * You can't clone to a different type. */ if (clone_parent->os->os_phys->os_type != type) { dsl_dir_close(pdd, FTAG); return (EINVAL); } oa.clone_parent = clone_parent->os->os_dsl_dataset; } err = dsl_sync_task_do(pdd->dd_pool, dmu_objset_create_check, dmu_objset_create_sync, pdd, &oa, 5); dsl_dir_close(pdd, FTAG); return (err); }
/* * Find all 'allow' permissions from a given point and then continue * traversing up to the root. * * This function constructs an nvlist of nvlists. * each setpoint is an nvlist composed of an nvlist of an nvlist * of the individual * users/groups/everyone/create * permissions. * * The nvlist will look like this. * * { source fsname -> { whokeys { permissions,...}, ...}} * * The fsname nvpairs will be arranged in a bottom up order. For example, * if we have the following structure a/b/c then the nvpairs for the fsnames * will be ordered a/b/c, a/b, a. */ int dsl_deleg_get(const char *ddname, nvlist_t **nvp) { dsl_dir_t *dd, *startdd; dsl_pool_t *dp; int error; objset_t *mos; error = dsl_dir_open(ddname, FTAG, &startdd, NULL); if (error) return (error); dp = startdd->dd_pool; mos = dp->dp_meta_objset; VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); rw_enter(&dp->dp_config_rwlock, RW_READER); for (dd = startdd; dd != NULL; dd = dd->dd_parent) { zap_cursor_t basezc; zap_attribute_t baseza; nvlist_t *sp_nvp; uint64_t n; char source[MAXNAMELEN]; if (dd->dd_phys->dd_deleg_zapobj && (zap_count(mos, dd->dd_phys->dd_deleg_zapobj, &n) == 0) && n) { VERIFY(nvlist_alloc(&sp_nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); } else { continue; } for (zap_cursor_init(&basezc, mos, dd->dd_phys->dd_deleg_zapobj); zap_cursor_retrieve(&basezc, &baseza) == 0; zap_cursor_advance(&basezc)) { zap_cursor_t zc; zap_attribute_t za; nvlist_t *perms_nvp; ASSERT(baseza.za_integer_length == 8); ASSERT(baseza.za_num_integers == 1); VERIFY(nvlist_alloc(&perms_nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); for (zap_cursor_init(&zc, mos, baseza.za_first_integer); zap_cursor_retrieve(&zc, &za) == 0; zap_cursor_advance(&zc)) { VERIFY(nvlist_add_boolean(perms_nvp, za.za_name) == 0); } zap_cursor_fini(&zc); VERIFY(nvlist_add_nvlist(sp_nvp, baseza.za_name, perms_nvp) == 0); nvlist_free(perms_nvp); } zap_cursor_fini(&basezc); dsl_dir_name(dd, source); VERIFY(nvlist_add_nvlist(*nvp, source, sp_nvp) == 0); nvlist_free(sp_nvp); } rw_exit(&dp->dp_config_rwlock); dsl_dir_close(startdd, FTAG); return (0); }
/* * Check if user has requested permission. */ int dsl_deleg_access(const char *ddname, const char *perm, cred_t *cr) { dsl_dir_t *dd, *startdd; dsl_pool_t *dp; void *cookie; int error; char checkflag = ZFS_DELEG_LOCAL; const char *tail; objset_t *mos; avl_tree_t permsets; perm_set_t *setnode; /* * Use tail so that zfs_ioctl() code doesn't have * to always to to figure out parent name in order * to do access check. for example renaming a snapshot */ error = dsl_dir_open(ddname, FTAG, &startdd, &tail); if (error) return (error); if (tail && tail[0] != '@') { dsl_dir_close(startdd, FTAG); return (ENOENT); } dp = startdd->dd_pool; mos = dp->dp_meta_objset; if (dsl_delegation_on(mos) == B_FALSE) { dsl_dir_close(startdd, FTAG); return (ECANCELED); } if (spa_version(dmu_objset_spa(dp->dp_meta_objset)) < SPA_VERSION_DELEGATED_PERMS) { dsl_dir_close(startdd, FTAG); return (EPERM); } avl_create(&permsets, perm_set_compare, sizeof (perm_set_t), offsetof(perm_set_t, p_node)); rw_enter(&dp->dp_config_rwlock, RW_READER); for (dd = startdd; dd != NULL; dd = dd->dd_parent, checkflag = ZFS_DELEG_DESCENDENT) { uint64_t zapobj; boolean_t expanded; /* * If not in global zone then make sure * the zoned property is set */ if (!INGLOBALZONE(curproc)) { uint64_t zoned; if (dsl_prop_get_ds_locked(dd, zfs_prop_to_name(ZFS_PROP_ZONED), 8, 1, &zoned, NULL) != 0) break; if (!zoned) break; } zapobj = dd->dd_phys->dd_deleg_zapobj; if (zapobj == 0) continue; dsl_load_user_sets(mos, zapobj, &permsets, checkflag, cr); again: expanded = B_FALSE; for (setnode = avl_first(&permsets); setnode; setnode = AVL_NEXT(&permsets, setnode)) { if (setnode->p_matched == B_TRUE) continue; /* See if this set directly grants this permission */ error = dsl_check_access(mos, zapobj, ZFS_DELEG_NAMED_SET, 0, setnode->p_setname, perm); if (error == 0) goto success; if (error == EPERM) setnode->p_matched = B_TRUE; /* See if this set includes other sets */ error = dsl_load_sets(mos, zapobj, ZFS_DELEG_NAMED_SET_SETS, 0, setnode->p_setname, &permsets); if (error == 0) setnode->p_matched = expanded = B_TRUE; } /* * If we expanded any sets, that will define more sets, * which we need to check. */ if (expanded) goto again; error = dsl_check_user_access(mos, zapobj, perm, checkflag, cr); if (error == 0) goto success; } error = EPERM; success: rw_exit(&dp->dp_config_rwlock); dsl_dir_close(startdd, FTAG); cookie = NULL; while ((setnode = avl_destroy_nodes(&permsets, &cookie)) != NULL) kmem_free(setnode, sizeof (perm_set_t)); return (error); }