int dsl_dataset_get_holds(const char *dsname, nvlist_t *nvl) { dsl_pool_t *dp; dsl_dataset_t *ds; int err; err = dsl_pool_hold(dsname, FTAG, &dp); if (err != 0) return (err); err = dsl_dataset_hold(dp, dsname, FTAG, &ds); if (err != 0) { dsl_pool_rele(dp, FTAG); return (err); } if (dsl_dataset_phys(ds)->ds_userrefs_obj != 0) { zap_attribute_t *za; zap_cursor_t zc; za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); for (zap_cursor_init(&zc, ds->ds_dir->dd_pool->dp_meta_objset, dsl_dataset_phys(ds)->ds_userrefs_obj); zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { fnvlist_add_uint64(nvl, za->za_name, za->za_first_integer); } zap_cursor_fini(&zc); kmem_free(za, sizeof (zap_attribute_t)); } dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (0); }
/* called from zpl */ int dmu_objset_open(const char *name, dmu_objset_type_t type, int mode, objset_t **osp) { objset_t *os; dsl_dataset_t *ds; int err; ASSERT(DS_MODE_TYPE(mode) == DS_MODE_USER || DS_MODE_TYPE(mode) == DS_MODE_OWNER); os = kmem_alloc(sizeof (objset_t), KM_SLEEP); if (DS_MODE_TYPE(mode) == DS_MODE_USER) err = dsl_dataset_hold(name, os, &ds); else err = dsl_dataset_own(name, mode, os, &ds); if (err) { kmem_free(os, sizeof (objset_t)); return (err); } err = dmu_objset_open_ds_os(ds, os, type); if (err) { if (DS_MODE_TYPE(mode) == DS_MODE_USER) dsl_dataset_rele(ds, os); else dsl_dataset_disown(ds, os); kmem_free(os, sizeof (objset_t)); } else { os->os_mode = mode; *osp = os; } return (err); }
static int dsl_bookmark_create_check(void *arg, dmu_tx_t *tx) { dsl_bookmark_create_arg_t *dbca = arg; dsl_pool_t *dp = dmu_tx_pool(tx); int rv = 0; nvpair_t *pair; if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS)) return (SET_ERROR(ENOTSUP)); for (pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL); pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) { dsl_dataset_t *snapds; int error; /* note: validity of nvlist checked by ioctl layer */ error = dsl_dataset_hold(dp, fnvpair_value_string(pair), FTAG, &snapds); if (error == 0) { error = dsl_bookmark_create_check_impl(snapds, nvpair_name(pair), tx); dsl_dataset_rele(snapds, FTAG); } if (error != 0) { fnvlist_add_int32(dbca->dbca_errors, nvpair_name(pair), error); rv = error; } } return (rv); }
static void dmu_objset_clone_sync(void *arg, dmu_tx_t *tx) { dmu_objset_clone_arg_t *doca = arg; dsl_pool_t *dp = dmu_tx_pool(tx); dsl_dir_t *pdd; const char *tail; dsl_dataset_t *origin, *ds; uint64_t obj; char namebuf[MAXNAMELEN]; VERIFY0(dsl_dir_hold(dp, doca->doca_clone, FTAG, &pdd, &tail)); VERIFY0(dsl_dataset_hold(dp, doca->doca_origin, FTAG, &origin)); obj = dsl_dataset_create_sync(pdd, tail, origin, 0, doca->doca_cred, tx); VERIFY0(dsl_dataset_hold_obj(pdd->dd_pool, obj, FTAG, &ds)); dsl_dataset_name(origin, namebuf); spa_history_log_internal_ds(ds, "clone", tx, "origin=%s (%llu)", namebuf, origin->ds_object); dsl_dataset_rele(ds, FTAG); dsl_dataset_rele(origin, FTAG); dsl_dir_rele(pdd, FTAG); }
/* ARGSUSED */ int dmu_objset_prefetch(char *name, void *arg) { dsl_dataset_t *ds; if (dsl_dataset_hold(name, FTAG, &ds)) return (0); if (!BP_IS_HOLE(&ds->ds_phys->ds_bp)) { mutex_enter(&ds->ds_opening_lock); if (!dsl_dataset_get_user_ptr(ds)) { uint32_t aflags = ARC_NOWAIT | ARC_PREFETCH; zbookmark_t zb; zb.zb_objset = ds->ds_object; zb.zb_object = 0; zb.zb_level = -1; zb.zb_blkid = 0; (void) arc_read_nolock(NULL, dsl_dataset_get_spa(ds), &ds->ds_phys->ds_bp, NULL, NULL, ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL | ZIO_FLAG_SPECULATIVE, &aflags, &zb); } mutex_exit(&ds->ds_opening_lock); } dsl_dataset_rele(ds, FTAG); return (0); }
static void dsl_dataset_user_hold_sync(void *arg, dmu_tx_t *tx) { dsl_dataset_user_hold_arg_t *dduha = arg; dsl_pool_t *dp = dmu_tx_pool(tx); nvlist_t *tmpholds; nvpair_t *pair; uint64_t now = gethrestime_sec(); if (dduha->dduha_minor != 0) tmpholds = fnvlist_alloc(); else tmpholds = NULL; for (pair = nvlist_next_nvpair(dduha->dduha_chkholds, NULL); pair != NULL; pair = nvlist_next_nvpair(dduha->dduha_chkholds, pair)) { dsl_dataset_t *ds; VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds)); dsl_dataset_user_hold_sync_one_impl(tmpholds, ds, fnvpair_value_string(pair), dduha->dduha_minor, now, tx); dsl_dataset_rele(ds, FTAG); } dsl_onexit_hold_cleanup(dp->dp_spa, tmpholds, dduha->dduha_minor); }
/* * Note: will longjmp (via lua_error()) on error. * Assumes that the dsname is argument #1 (for error reporting purposes). */ dsl_dataset_t * zcp_dataset_hold(lua_State *state, dsl_pool_t *dp, const char *dsname, void *tag) { dsl_dataset_t *ds; int error = dsl_dataset_hold(dp, dsname, tag, &ds); (void) zcp_dataset_hold_error(state, dp, dsname, error); return (ds); }
static void dsl_bookmark_create_sync(void *arg, dmu_tx_t *tx) { dsl_bookmark_create_arg_t *dbca = arg; dsl_pool_t *dp = dmu_tx_pool(tx); objset_t *mos = dp->dp_meta_objset; nvpair_t *pair; ASSERT(spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS)); for (pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL); pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) { dsl_dataset_t *snapds, *bmark_fs; zfs_bookmark_phys_t bmark_phys; char *shortname; VERIFY0(dsl_dataset_hold(dp, fnvpair_value_string(pair), FTAG, &snapds)); VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair), &bmark_fs, FTAG, &shortname)); if (bmark_fs->ds_bookmarks == 0) { bmark_fs->ds_bookmarks = zap_create_norm(mos, U8_TEXTPREP_TOUPPER, DMU_OTN_ZAP_METADATA, DMU_OT_NONE, 0, tx); spa_feature_incr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx); dsl_dataset_zapify(bmark_fs, tx); VERIFY0(zap_add(mos, bmark_fs->ds_object, DS_FIELD_BOOKMARK_NAMES, sizeof (bmark_fs->ds_bookmarks), 1, &bmark_fs->ds_bookmarks, tx)); } bmark_phys.zbm_guid = dsl_dataset_phys(snapds)->ds_guid; bmark_phys.zbm_creation_txg = dsl_dataset_phys(snapds)->ds_creation_txg; bmark_phys.zbm_creation_time = dsl_dataset_phys(snapds)->ds_creation_time; VERIFY0(zap_add(mos, bmark_fs->ds_bookmarks, shortname, sizeof (uint64_t), sizeof (zfs_bookmark_phys_t) / sizeof (uint64_t), &bmark_phys, tx)); spa_history_log_internal_ds(bmark_fs, "bookmark", tx, "name=%s creation_txg=%llu target_snap=%llu", shortname, (longlong_t)bmark_phys.zbm_creation_txg, (longlong_t)snapds->ds_object); dsl_dataset_rele(bmark_fs, FTAG); dsl_dataset_rele(snapds, FTAG); } }
/*ARGSUSED*/ static int dmu_objset_clone_check(void *arg, dmu_tx_t *tx) { dmu_objset_clone_arg_t *doca = arg; dsl_dir_t *pdd; const char *tail; int error; dsl_dataset_t *origin; dsl_pool_t *dp = dmu_tx_pool(tx); if (strchr(doca->doca_clone, '@') != NULL) return (SET_ERROR(EINVAL)); error = dsl_dir_hold(dp, doca->doca_clone, FTAG, &pdd, &tail); if (error != 0) return (error); if (tail == NULL) { dsl_dir_rele(pdd, FTAG); return (SET_ERROR(EEXIST)); } /* You can't clone across pools. */ if (pdd->dd_pool != dp) { dsl_dir_rele(pdd, FTAG); return (SET_ERROR(EXDEV)); } error = dsl_fs_ss_limit_check(pdd, 1, ZFS_PROP_FILESYSTEM_LIMIT, NULL, doca->doca_cred); if (error != 0) { dsl_dir_rele(pdd, FTAG); return (SET_ERROR(EDQUOT)); } dsl_dir_rele(pdd, FTAG); error = dsl_dataset_hold(dp, doca->doca_origin, FTAG, &origin); if (error != 0) return (error); /* You can't clone across pools. */ if (origin->ds_dir->dd_pool != dp) { dsl_dataset_rele(origin, FTAG); return (SET_ERROR(EXDEV)); } /* You can only clone snapshots, not the head datasets. */ if (!dsl_dataset_is_snapshot(origin)) { dsl_dataset_rele(origin, FTAG); return (SET_ERROR(EINVAL)); } dsl_dataset_rele(origin, FTAG); return (0); }
static int dsl_dataset_user_hold_check(void *arg, dmu_tx_t *tx) { dsl_dataset_user_hold_arg_t *dduha = arg; dsl_pool_t *dp = dmu_tx_pool(tx); nvpair_t *pair; if (spa_version(dp->dp_spa) < SPA_VERSION_USERREFS) return (SET_ERROR(ENOTSUP)); if (!dmu_tx_is_syncing(tx)) return (0); for (pair = nvlist_next_nvpair(dduha->dduha_holds, NULL); pair != NULL; pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) { dsl_dataset_t *ds; int error = 0; char *htag, *name; /* must be a snapshot */ name = nvpair_name(pair); if (strchr(name, '@') == NULL) error = SET_ERROR(EINVAL); if (error == 0) error = nvpair_value_string(pair, &htag); if (error == 0) error = dsl_dataset_hold(dp, name, FTAG, &ds); if (error == 0) { error = dsl_dataset_user_hold_check_one(ds, htag, dduha->dduha_minor != 0, tx); dsl_dataset_rele(ds, FTAG); } if (error == 0) { fnvlist_add_string(dduha->dduha_chkholds, name, htag); } else { /* * We register ENOENT errors so they can be correctly * reported if needed, such as when all holds fail. */ fnvlist_add_int32(dduha->dduha_errlist, name, error); if (error != ENOENT) return (error); } } return (0); }
int dsl_deleg_access(const char *dsname, const char *perm, cred_t *cr) { dsl_dataset_t *ds; int error; error = dsl_dataset_hold(dsname, FTAG, &ds); if (error) return (error); error = dsl_deleg_access_impl(ds, B_FALSE, perm, cr); dsl_dataset_rele(ds, FTAG); return (error); }
/* called from zpl */ int dmu_objset_hold(const char *name, void *tag, objset_t **osp) { dsl_dataset_t *ds; int err; err = dsl_dataset_hold(name, tag, &ds); if (err) return (err); err = dmu_objset_from_ds(ds, osp); if (err) dsl_dataset_rele(ds, tag); return (err); }
int dsl_dataset_keystatus_byname(dsl_pool_t *dp, const char *dsname, zfs_crypt_key_status_t *keystatus) { dsl_dataset_t *ds; int error; error = dsl_dataset_hold(dp, dsname, FTAG, &ds); if (error != 0) { return (error); } *keystatus = dsl_dataset_keystatus(ds, B_FALSE); dsl_dataset_rele(ds, FTAG); return (0); }
static void dsl_dataset_user_hold_sync(void *arg, dmu_tx_t *tx) { dsl_dataset_user_hold_arg_t *dduha = arg; dsl_pool_t *dp = dmu_tx_pool(tx); nvpair_t *pair; uint64_t now = gethrestime_sec(); for (pair = nvlist_next_nvpair(dduha->dduha_holds, NULL); pair != NULL; pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) { dsl_dataset_t *ds; VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds)); dsl_dataset_user_hold_sync_one(ds, fnvpair_value_string(pair), dduha->dduha_minor, now, tx); dsl_dataset_rele(ds, FTAG); } }
static int dsl_destroy_snapshot_check(void *arg, dmu_tx_t *tx) { dmu_snapshots_destroy_arg_t *dsda = arg; dsl_pool_t *dp = dmu_tx_pool(tx); nvpair_t *pair; int error = 0; if (!dmu_tx_is_syncing(tx)) return (0); for (pair = nvlist_next_nvpair(dsda->dsda_snaps, NULL); pair != NULL; pair = nvlist_next_nvpair(dsda->dsda_snaps, pair)) { dsl_dataset_t *ds; error = dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds); /* * If the snapshot does not exist, silently ignore it * (it's "already destroyed"). */ if (error == ENOENT) continue; if (error == 0) { error = dsl_destroy_snapshot_check_impl(ds, dsda->dsda_defer); dsl_dataset_rele(ds, FTAG); } if (error == 0) { fnvlist_add_boolean(dsda->dsda_successful_snaps, nvpair_name(pair)); } else { fnvlist_add_int32(dsda->dsda_errlist, nvpair_name(pair), error); } } pair = nvlist_next_nvpair(dsda->dsda_errlist, NULL); if (pair != NULL) return (fnvpair_value_int32(pair)); return (0); }
int dsl_prop_get(const char *dsname, const char *propname, int intsz, int numints, void *buf, char *setpoint) { dsl_dataset_t *ds; int err; err = dsl_dataset_hold(dsname, FTAG, &ds); if (err) return (err); rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER); err = dsl_prop_get_ds(ds, propname, intsz, numints, buf, setpoint); rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock); dsl_dataset_rele(ds, FTAG); return (err); }
static int dsl_dataset_user_release_check(void *arg, dmu_tx_t *tx) { dsl_dataset_user_release_arg_t *ddura = arg; dsl_pool_t *dp = dmu_tx_pool(tx); nvpair_t *pair; int rv = 0; if (!dmu_tx_is_syncing(tx)) return (0); for (pair = nvlist_next_nvpair(ddura->ddura_holds, NULL); pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) { const char *name = nvpair_name(pair); int error; dsl_dataset_t *ds; nvlist_t *holds; error = nvpair_value_nvlist(pair, &holds); if (error != 0) return (EINVAL); error = dsl_dataset_hold(dp, name, FTAG, &ds); if (error == 0) { boolean_t deleteme; error = dsl_dataset_user_release_check_one(ds, holds, &deleteme); if (error == 0 && deleteme) { fnvlist_add_boolean(ddura->ddura_todelete, name); } dsl_dataset_rele(ds, FTAG); } if (error != 0) { if (ddura->ddura_errlist != NULL) { fnvlist_add_int32(ddura->ddura_errlist, name, error); } rv = error; } } return (rv); }
static int dsl_bookmark_hold_ds(dsl_pool_t *dp, const char *fullname, dsl_dataset_t **dsp, void *tag, char **shortnamep) { char buf[MAXNAMELEN]; char *hashp; if (strlen(fullname) >= MAXNAMELEN) return (SET_ERROR(ENAMETOOLONG)); hashp = strchr(fullname, '#'); if (hashp == NULL) return (SET_ERROR(EINVAL)); *shortnamep = hashp + 1; if (zfs_component_namecheck(*shortnamep, NULL, NULL)) return (SET_ERROR(EINVAL)); (void) strlcpy(buf, fullname, hashp - fullname + 1); return (dsl_dataset_hold(dp, buf, tag, dsp)); }
int dsl_deleg_access(const char *dsname, const char *perm, cred_t *cr) { dsl_pool_t *dp; dsl_dataset_t *ds; int error; error = dsl_pool_hold(dsname, FTAG, &dp); if (error != 0) return (error); error = dsl_dataset_hold(dp, dsname, FTAG, &ds); if (error == 0) { error = dsl_deleg_access_impl(ds, perm, cr); dsl_dataset_rele(ds, FTAG); } dsl_pool_rele(dp, FTAG); return (error); }
static int dsl_dataset_user_hold_check(void *arg, dmu_tx_t *tx) { dsl_dataset_user_hold_arg_t *dduha = arg; dsl_pool_t *dp = dmu_tx_pool(tx); nvpair_t *pair; int rv = 0; if (spa_version(dp->dp_spa) < SPA_VERSION_USERREFS) return (ENOTSUP); for (pair = nvlist_next_nvpair(dduha->dduha_holds, NULL); pair != NULL; pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) { int error = 0; dsl_dataset_t *ds; char *htag; /* must be a snapshot */ if (strchr(nvpair_name(pair), '@') == NULL) error = EINVAL; if (error == 0) error = nvpair_value_string(pair, &htag); if (error == 0) { error = dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds); } if (error == 0) { error = dsl_dataset_user_hold_check_one(ds, htag, dduha->dduha_minor != 0, tx); dsl_dataset_rele(ds, FTAG); } if (error != 0) { rv = error; fnvlist_add_int32(dduha->dduha_errlist, nvpair_name(pair), error); } } return (rv); }
/* * Retrieve the bookmarks that exist in the specified dataset, and the * requested properties of each bookmark. * * The "props" nvlist specifies which properties are requested. * See lzc_get_bookmarks() for the list of valid properties. */ int dsl_get_bookmarks(const char *dsname, nvlist_t *props, nvlist_t *outnvl) { dsl_pool_t *dp; dsl_dataset_t *ds; int err; err = dsl_pool_hold(dsname, FTAG, &dp); if (err != 0) return (err); err = dsl_dataset_hold(dp, dsname, FTAG, &ds); if (err != 0) { dsl_pool_rele(dp, FTAG); return (err); } err = dsl_get_bookmarks_impl(ds, props, outnvl); dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (err); }
/* * Holds the pool while the objset is held. Therefore only one objset * can be held at a time. */ int dmu_objset_hold(const char *name, void *tag, objset_t **osp) { dsl_pool_t *dp; dsl_dataset_t *ds; int err; err = dsl_pool_hold(name, tag, &dp); if (err != 0) return (err); err = dsl_dataset_hold(dp, name, tag, &ds); if (err != 0) { dsl_pool_rele(dp, tag); return (err); } err = dmu_objset_from_ds(ds, osp); if (err != 0) { dsl_dataset_rele(ds, tag); dsl_pool_rele(dp, tag); } return (err); }
static void dsl_dataset_user_release_sync(void *arg, dmu_tx_t *tx) { dsl_dataset_user_release_arg_t *ddura = arg; dsl_pool_t *dp = dmu_tx_pool(tx); nvpair_t *pair; for (pair = nvlist_next_nvpair(ddura->ddura_holds, NULL); pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) { dsl_dataset_t *ds; VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds)); dsl_dataset_user_release_sync_one(ds, fnvpair_value_nvlist(pair), tx); if (nvlist_exists(ddura->ddura_todelete, nvpair_name(pair))) { ASSERT(ds->ds_userrefs == 0 && ds->ds_phys->ds_num_children == 1 && DS_IS_DEFER_DESTROY(ds)); dsl_destroy_snapshot_sync_impl(ds, B_FALSE, tx); } dsl_dataset_rele(ds, FTAG); } }
static int dsl_crypto_key_change_find(const char *dsname, void *arg) { struct wkey_change_arg *ca = arg; struct kcnode *kcn; dsl_dataset_t *ds; objset_t *os; uint64_t crypt; char caource[MAXNAMELEN]; char setpoint[MAXNAMELEN]; int err; dsl_pool_t *dp; err = dsl_pool_hold(dsname, FTAG, &dp); if (err != 0) return (err); kcn = kmem_alloc(sizeof (struct kcnode), KM_SLEEP); if ((err = dsl_dataset_hold(dp, dsname, kcn, &ds)) != 0) { kmem_free(kcn, sizeof (struct kcnode)); dsl_pool_rele(dp, FTAG); return (err); } if ((err = dmu_objset_from_ds(ds, &os)) != 0) { dsl_dataset_rele(ds, kcn); dsl_pool_rele(dp, FTAG); kmem_free(kcn, sizeof (struct kcnode)); return (err); } /* * Check that this child dataset of ca->parent * is actually inheriting keysource from ca->parent and * not somewhere else (eg local, or some other dataset). */ rrw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER, FTAG); VERIFY(dsl_prop_get_ds(ds, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), 8, 1, &crypt, NULL/*, DSL_PROP_GET_EFFECTIVE*/) == 0); VERIFY(dsl_prop_get_ds(ds, zfs_prop_to_name(ZFS_PROP_KEYSOURCE), 1, sizeof (caource), &caource, setpoint/*, DSL_PROP_GET_EFFECTIVE*/) == 0); rrw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock, FTAG); if (crypt == ZIO_CRYPT_OFF || ((strcmp(ca->ca_parent, setpoint) != 0 && strcmp(ca->ca_parent, dsname) != 0))) { dsl_dataset_rele(ds, kcn); dsl_pool_rele(dp, FTAG); kmem_free(kcn, sizeof (struct kcnode)); return (0); } //dsl_sync_task_create(ca->ca_dstg, dsl_crypto_key_change_check, // dsl_crypto_key_change_sync, ds, arg, 1); ca->ca_ds = ds; err = dsl_sync_task(dsname, dsl_crypto_key_change_check, dsl_crypto_key_change_sync, arg, 1, ZFS_SPACE_CHECK_NONE); kcn->kc_ds = ds; list_insert_tail(&ca->ca_nodes, kcn); return (0); }
int dsl_crypto_key_change(char *dsname, zcrypt_key_t *newkey, nvlist_t *props) { struct wkey_change_arg *ca; struct kcnode *kcn; dsl_dataset_t *ds; dsl_props_arg_t pa; spa_t *spa; int err; //dsl_sync_task_group_t *dstg; zcrypt_key_t *oldkey; dsl_pool_t *dp; ASSERT(newkey != NULL); ASSERT(dsname != NULL); err = dsl_pool_hold(dsname, FTAG, &dp); if (err != 0) return (err); if ((err = dsl_dataset_hold(dp, dsname, FTAG, &ds)) != 0) { dsl_pool_rele(dp, FTAG); return (err); } /* * Take the spa lock here so that new datasets can't get * created below us while we are doing a wrapping key change. * This is to avoid them being created with the wrong inherited * wrapping key. */ err = spa_open(dsname, &spa, FTAG); if (err) return (err); oldkey = zcrypt_key_copy(zcrypt_keystore_find_wrappingkey(spa, ds->ds_object)); if (oldkey == NULL) { dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); spa_close(spa, FTAG); return (ENOENT); } ca = kmem_alloc(sizeof (struct wkey_change_arg), KM_SLEEP); ca->ca_new_key = newkey; ca->ca_old_key = oldkey; ca->ca_parent = dsname; ca->ca_props = props; list_create(&ca->ca_nodes, sizeof (struct kcnode), offsetof(struct kcnode, kc_node)); zcrypt_key_hold(ca->ca_old_key, FTAG); zcrypt_key_hold(ca->ca_new_key, FTAG); //ca->ca_ds = dsl_sync_task_group_create(spa_get_dsl(spa)); err = dmu_objset_find(dsname, dsl_crypto_key_change_find, ca, DS_FIND_CHILDREN); /* * If this is the "top" dataset in this keychange it gets * the keysource and salt properties updated. */ pa.pa_props = props; pa.pa_source = ZPROP_SRC_LOCAL; //pa.pa_flags = 0; //pa.pa_zone = curzone; //dsl_sync_task_create(ca->ca_dstg, NULL, dsl_props_set_sync, ds, &pa, 2); dsl_props_set(dsname, ZPROP_SRC_LOCAL, props); //if (err == 0) //err = dsl_sync_task_group_wait(dstg); while ((kcn = list_head(&ca->ca_nodes))) { list_remove(&ca->ca_nodes, kcn); dsl_dataset_rele(kcn->kc_ds, kcn); kmem_free(kcn, sizeof (struct kcnode)); } //dsl_sync_task_group_destroy(ca->ca_dstg); /* * We are finished so release and free both the old and new keys. * We can free even the new key because everyone got a copy of it * not a reference to this one. */ zcrypt_key_release(ca->ca_old_key, FTAG); zcrypt_key_free(ca->ca_old_key); zcrypt_key_release(ca->ca_new_key, FTAG); zcrypt_key_free(ca->ca_new_key); kmem_free(ca, sizeof (struct wkey_change_arg)); dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); spa_close(spa, FTAG); return (err); }
int dsl_crypto_key_new(const char *dsname) { dsl_dataset_t *ds; objset_t *os; zcrypt_keystore_node_t *skn; spa_t *spa; struct knarg arg; int error; dsl_pool_t *dp; void *cookie; error = dsl_pool_hold(dsname, FTAG, &dp); if (error != 0) return (error); if ((error = dsl_dataset_hold(dp, dsname, FTAG, &ds)) != 0) { dsl_pool_rele(dp, FTAG); return (error); } if (dsl_dataset_is_snapshot(ds)) { dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (ENOTSUP); } if ((error = dmu_objset_from_ds(ds, &os)) != 0) { dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (error); } if (os->os_crypt == ZIO_CRYPT_OFF) { dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (ENOTSUP); } ASSERT(os->os_crypt != ZIO_CRYPT_INHERIT); /* * Need the keychain and wrapping key to already be available. */ spa = dsl_dataset_get_spa(ds); skn = zcrypt_keystore_find_node(spa, ds->ds_object, B_FALSE); if (skn == NULL) { dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (ENOENT); } ASSERT(ds != NULL); ASSERT(ds->ds_objset != NULL); //zil_suspend_dmu_sync(dmu_objset_zil(os)); zil_suspend(dsname, &cookie); arg.kn_skn = skn; arg.kn_txgkey = zcrypt_key_gen(os->os_crypt); arg.kn_ds = ds; zcrypt_key_hold(skn->skn_wrapkey, FTAG); VERIFY(zcrypt_wrap_key(skn->skn_wrapkey, arg.kn_txgkey, &arg.kn_wkeybuf, &arg.kn_wkeylen, zio_crypt_select_wrap(os->os_crypt)) == 0); error = dsl_sync_task(spa->spa_name, dsl_crypto_key_new_check, dsl_crypto_key_new_sync, &arg, 1, ZFS_SPACE_CHECK_NONE); kmem_free(arg.kn_wkeybuf, arg.kn_wkeylen); zcrypt_key_release(skn->skn_wrapkey, FTAG); //zil_resume_dmu_sync(dmu_objset_zil(os)); zil_resume(os); dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); if (error) zcrypt_key_free(arg.kn_txgkey); return (error); }
int dsl_crypto_key_inherit(const char *dsname) { char keysource[MAXNAMELEN]; char setpoint[MAXNAMELEN]; dsl_dataset_t *ids; int error; zcrypt_key_t *wrappingkey; zfs_crypt_key_status_t keystatus; spa_t *spa; dsl_pool_t *dp; /* * Try inheriting the wrapping key from our parent */ error = dsl_pool_hold(dsname, FTAG, &dp); if (error != 0) return (error); error = dsl_dataset_keystatus_byname(dp, dsname, &keystatus); if (error != 0) { dsl_pool_rele(dp, FTAG); return (error); } if (keystatus == ZFS_CRYPT_KEY_NONE) { dsl_pool_rele(dp, FTAG); return (0); } if (keystatus == ZFS_CRYPT_KEY_AVAILABLE) { dsl_pool_rele(dp, FTAG); return (EEXIST); } error = dsl_prop_get(dsname, zfs_prop_to_name(ZFS_PROP_KEYSOURCE), 1, sizeof (keysource), &keysource, setpoint); if (error != 0) { dsl_pool_rele(dp, FTAG); return (error); } if (strcmp(setpoint, dsname) == 0) { dsl_pool_rele(dp, FTAG); return (ENOENT); } error = dsl_dataset_hold(dp, setpoint, FTAG, &ids); if (error != 0) { dsl_pool_rele(dp, FTAG); return (error); } spa = dsl_dataset_get_spa(ids); wrappingkey = zcrypt_key_copy(zcrypt_keystore_find_wrappingkey(spa, ids->ds_object)); dsl_dataset_rele(ids, FTAG); dsl_pool_rele(dp, FTAG); if (wrappingkey == NULL) return (ENOENT); error = dsl_crypto_key_load(dsname, wrappingkey); return (error); }
int dsl_crypto_key_load(const char *dsname, zcrypt_key_t *wrappingkey) { dsl_dataset_t *ds; uint64_t crypt; int error; dsl_pool_t *dp; error = dsl_pool_hold(dsname, FTAG, &dp); if (error != 0) return (error); if ((error = dsl_dataset_hold(dp, dsname, FTAG, &ds)) != 0) { dsl_pool_rele(dp, FTAG); return (error); } /* * This is key load not key change so if ds->ds_key is already * set we fail. */ if (zcrypt_keystore_find_node(dsl_dataset_get_spa(ds), ds->ds_object, B_FALSE) != NULL) { dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (EEXIST); } /* * Find out what size of key we expect. * * For now the wrapping key size (and type) matches the size * of the dataset key, this may not always be the case * (particularly if we ever support wrapping dataset keys * with asymmetric keys (eg RSA)). * * When alternate wrapping keys are added it maybe done using * a index property. */ rrw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER, FTAG); error = dsl_prop_get_ds(ds, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), 8, 1, &crypt, NULL/*, DSL_PROP_GET_EFFECTIVE*/); rrw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock, FTAG); if (error != 0) { dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (error); } if (crypt == ZIO_CRYPT_OFF) { dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (ENOTSUP); } ASSERT(crypt != ZIO_CRYPT_INHERIT); error = dsl_keychain_load(ds, crypt, wrappingkey); dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (error); }
/* * dsl_crypto_key_unload * * Remove the key from the in memory keystore. * * First we have to remove the minor node for a ZVOL or unmount * the filesystem. This is so that we flush all pending IO for it to disk * so we won't need to encrypt anything with this key. Anything in flight * should already have a lock on the keys it needs. * We can't assume that userland has already successfully unmounted the * dataset though in many cases it will have. * * If the key can't be removed return the failure back to our caller. */ int dsl_crypto_key_unload(const char *dsname) { dsl_dataset_t *ds; objset_t *os; int error; spa_t *spa; dsl_pool_t *dp; #ifdef _KERNEL dmu_objset_type_t os_type; //vfs_t *vfsp; struct vfsmount *vfsp; #endif /* _KERNEL */ error = dsl_pool_hold(dsname, FTAG, &dp); if (error != 0) return (error); /* XXX - should we use own_exclusive() here? */ if ((error = dsl_dataset_hold(dp, dsname, FTAG, &ds)) != 0) { dsl_pool_rele(dp, FTAG); return (error); } if ((error = dmu_objset_from_ds(ds, &os)) != 0) { dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (error); } #ifdef _KERNEL /* * Make sure that the device node has gone for ZVOLs * and that filesystems are umounted. */ #if 0 // FIXME os_type = dmu_objset_type(os); if (os_type == DMU_OST_ZVOL) { error = zvol_remove_minor(dsname); if (error == ENXIO) error = 0; } else if (os_type == DMU_OST_ZFS) { vfsp = zfs_get_vfs(dsname); if (vfsp != NULL) { error = vn_vfswlock(vfsp->vfs_vnodecovered); VFS_RELE(vfsp); if (error == 0) error = dounmount(vfsp, 0, CRED()); } } if (error != 0) { dsl_dataset_rele(ds, FTAG); return (error); } #endif #endif /* _KERNEL */ /* * Make sure all dbufs are synced. * * It is essential for encrypted datasets to ensure that * there is no further pending IO before removing the key. */ if (dmu_objset_is_dirty(os, 0)) // FIXME, 0? txg_wait_synced(dmu_objset_pool(os), 0); dmu_objset_evict_dbufs(os); spa = dsl_dataset_get_spa(ds); error = zcrypt_keystore_remove(spa, ds->ds_object); dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); return (error); }
/* * Check if user has requested permission. */ int dsl_deleg_access(const char *dsname, const char *perm, cred_t *cr) { dsl_dataset_t *ds; dsl_dir_t *dd; dsl_pool_t *dp; void *cookie; int error; char checkflag; objset_t *mos; avl_tree_t permsets; perm_set_t *setnode; error = dsl_dataset_hold(dsname, FTAG, &ds); if (error) return (error); dp = ds->ds_dir->dd_pool; mos = dp->dp_meta_objset; if (dsl_delegation_on(mos) == B_FALSE) { dsl_dataset_rele(ds, FTAG); return (ECANCELED); } if (spa_version(dmu_objset_spa(dp->dp_meta_objset)) < SPA_VERSION_DELEGATED_PERMS) { dsl_dataset_rele(ds, FTAG); return (EPERM); } if (dsl_dataset_is_snapshot(ds)) { /* * Snapshots are treated as descendents only, * local permissions do not apply. */ checkflag = ZFS_DELEG_DESCENDENT; } else { checkflag = ZFS_DELEG_LOCAL; } 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 = ds->ds_dir; 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_dd(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_dataset_rele(ds, FTAG); cookie = NULL; while ((setnode = avl_destroy_nodes(&permsets, &cookie)) != NULL) kmem_free(setnode, sizeof (perm_set_t)); return (error); }