/* ARGSUSED */ static int dsl_prop_notify_all_cb(dsl_pool_t *dp, dsl_dataset_t *ds, void *arg) { dsl_dir_t *dd = ds->ds_dir; dsl_prop_record_t *pr; dsl_prop_cb_record_t *cbr; mutex_enter(&dd->dd_lock); for (pr = list_head(&dd->dd_props); pr; pr = list_next(&dd->dd_props, pr)) { for (cbr = list_head(&pr->pr_cbs); cbr; cbr = list_next(&pr->pr_cbs, cbr)) { uint64_t value; /* * Callback entries do not have holds on their * datasets so that datasets with registered * callbacks are still eligible for eviction. * Unlike operations to update properties on a * single dataset, we are performing a recursive * descent of related head datasets. The caller * of this function only has a dataset hold on * the passed in head dataset, not the snapshots * associated with this dataset. Without a hold, * the dataset pointer within callback records * for snapshots can be invalidated by eviction * at any time. * * Use dsl_dataset_try_add_ref() to verify * that the dataset for a snapshot has not * begun eviction processing and to prevent * eviction from occurring for the duration of * the callback. If the hold attempt fails, * this object is already being evicted and the * callback can be safely ignored. */ if (ds != cbr->cbr_ds && !dsl_dataset_try_add_ref(dp, cbr->cbr_ds, FTAG)) continue; if (dsl_prop_get_ds(cbr->cbr_ds, cbr->cbr_pr->pr_propname, sizeof (value), 1, &value, NULL) == 0) cbr->cbr_func(cbr->cbr_arg, value); if (ds != cbr->cbr_ds) dsl_dataset_rele(cbr->cbr_ds, FTAG); } } mutex_exit(&dd->dd_lock); return (0); }
int dsl_prop_get(const char *dsname, const char *propname, int intsz, int numints, void *buf, char *setpoint) { objset_t *os; int error; error = dmu_objset_hold(dsname, FTAG, &os); if (error != 0) return (error); error = dsl_prop_get_ds(dmu_objset_ds(os), propname, intsz, numints, buf, setpoint); dmu_objset_rele(os, FTAG); return (error); }
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); }
/* ARGSUSED */ static int dsl_prop_notify_all_cb(dsl_pool_t *dp, dsl_dataset_t *ds, void *arg) { dsl_dir_t *dd = ds->ds_dir; dsl_prop_cb_record_t *cbr; mutex_enter(&dd->dd_lock); for (cbr = list_head(&dd->dd_prop_cbs); cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { uint64_t value; if (dsl_prop_get_ds(cbr->cbr_ds, cbr->cbr_propname, sizeof (value), 1, &value, NULL) == 0) cbr->cbr_func(cbr->cbr_arg, value); } mutex_exit(&dd->dd_lock); return (0); }
/* ARGSUSED */ static int dsl_prop_notify_all_cb(dsl_pool_t *dp, dsl_dataset_t *ds, void *arg) { dsl_dir_t *dd = ds->ds_dir; dsl_prop_cb_record_t *cbr; mutex_enter(&dd->dd_lock); for (cbr = list_head(&dd->dd_prop_cbs); cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { uint64_t value; /* * Callback entries do not have holds on their datasets * so that datasets with registered callbacks are still * eligible for eviction. Unlike operations on callbacks * for a single dataset, we are performing a recursive * descent of related datasets and the calling context * for this iteration only has a dataset hold on the root. * Without a hold, the callback's pointer to the dataset * could be invalidated by eviction at any time. * * Use dsl_dataset_try_add_ref() to verify that the * dataset has not begun eviction processing and to * prevent eviction from occurring for the duration * of the callback. If the hold attempt fails, this * object is already being evicted and the callback can * be safely ignored. */ if (!dsl_dataset_try_add_ref(dp, cbr->cbr_ds, FTAG)) continue; if (dsl_prop_get_ds(cbr->cbr_ds, cbr->cbr_propname, sizeof (value), 1, &value, NULL) == 0) cbr->cbr_func(cbr->cbr_arg, value); dsl_dataset_rele(cbr->cbr_ds, FTAG); } mutex_exit(&dd->dd_lock); return (0); }
/* * Register interest in the named property. We'll call the callback * once to notify it of the current property value, and again each time * the property changes, until this callback is unregistered. * * Return 0 on success, errno if the prop is not an integer value. */ int dsl_prop_register(dsl_dataset_t *ds, const char *propname, dsl_prop_changed_cb_t *callback, void *cbarg) { dsl_dir_t *dd = ds->ds_dir; dsl_pool_t *dp = dd->dd_pool; uint64_t value; dsl_prop_cb_record_t *cbr; int err; int need_rwlock; need_rwlock = !RW_WRITE_HELD(&dp->dp_config_rwlock); if (need_rwlock) rw_enter(&dp->dp_config_rwlock, RW_READER); err = dsl_prop_get_ds(ds, propname, 8, 1, &value, NULL); if (err != 0) { if (need_rwlock) rw_exit(&dp->dp_config_rwlock); return (err); } cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP); cbr->cbr_ds = ds; cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP); (void) strcpy((char *)cbr->cbr_propname, propname); cbr->cbr_func = callback; cbr->cbr_arg = cbarg; mutex_enter(&dd->dd_lock); list_insert_head(&dd->dd_prop_cbs, cbr); mutex_exit(&dd->dd_lock); cbr->cbr_func(cbr->cbr_arg, value); VERIFY(0 == dsl_dir_open_obj(dp, dd->dd_object, NULL, cbr, &dd)); if (need_rwlock) rw_exit(&dp->dp_config_rwlock); /* Leave dir open until this callback is unregistered */ return (0); }
/* * Register interest in the named property. We'll call the callback * once to notify it of the current property value, and again each time * the property changes, until this callback is unregistered. * * Return 0 on success, errno if the prop is not an integer value. */ int dsl_prop_register(dsl_dataset_t *ds, const char *propname, dsl_prop_changed_cb_t *callback, void *cbarg) { dsl_dir_t *dd = ds->ds_dir; dsl_pool_t *dp = dd->dd_pool; uint64_t value; dsl_prop_cb_record_t *cbr; int err; int need_rwlock; need_rwlock = !RW_WRITE_HELD(&dp->dp_config_rwlock); if (need_rwlock) rw_enter(&dp->dp_config_rwlock, RW_READER); err = dsl_prop_get_ds(ds, propname, 8, 1, &value, NULL); if (err != 0) { if (need_rwlock) rw_exit(&dp->dp_config_rwlock); return (err); } cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_PUSHPAGE); cbr->cbr_ds = ds; cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_PUSHPAGE); (void) strlcpy((char *)cbr->cbr_propname, propname, MAXNAMELEN); cbr->cbr_func = callback; cbr->cbr_arg = cbarg; mutex_enter(&dd->dd_lock); list_insert_head(&dd->dd_prop_cbs, cbr); mutex_exit(&dd->dd_lock); cbr->cbr_func(cbr->cbr_arg, value); if (need_rwlock) rw_exit(&dp->dp_config_rwlock); return (0); }
int dsl_prop_get_int_ds(dsl_dataset_t *ds, const char *propname, uint64_t *valuep) { return (dsl_prop_get_ds(ds, propname, 8, 1, valuep, NULL)); }
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_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_clone * * Our caller (dmu_objset_create_sync) must have a lock on the dataset. */ int dsl_crypto_key_clone(dsl_dir_t *dd, dsl_dataset_phys_t *dsphys, uint64_t dsobj, dsl_dataset_t *clone_origin, dsl_crypto_ctx_t *ctx, dmu_tx_t *tx) { zcrypt_key_t *txgkey; zcrypt_key_t *wrappingkey = NULL; dsl_dataset_t *origin; uint64_t crypt; spa_t *spa = dd->dd_pool->dp_spa; int error = 0; ASSERT(ctx != NULL); if (BP_IS_HOLE(&dsphys->ds_bp)) { origin = ctx->dcc_origin_ds; } else { origin = clone_origin; } ASSERT(origin != NULL); /* * Need to look at the value of crypt on the origin snapshot * since it is sticky for encryption. */ VERIFY(dsl_prop_get_ds(origin, zfs_prop_to_name(ZFS_PROP_ENCRYPTION), 8, 1, &crypt,/* DSL_PROP_GET_EFFECTIVE,*/ NULL) == 0); if (crypt == ZIO_CRYPT_OFF) { return (0); } wrappingkey = ctx->dcc_wrap_key; dsl_keychain_create_obj(dd, tx); dsl_keychain_clone_phys(origin, dd, tx, wrappingkey); if (ctx->dcc_salt != 0) { objset_t *mos = dd->dd_pool->dp_meta_objset; uint64_t props_zapobj = dsl_dir_phys(dd)->dd_props_zapobj; error = zap_update(mos, props_zapobj, zfs_prop_to_name(ZFS_PROP_SALT), 8, 1, (void *)&ctx->dcc_salt, tx); } if (ctx->dcc_clone_newkey) { caddr_t wkeybuf; size_t wkeylen; /* * Generate a new key and add it to the keychain * to be valid from this txg onwards. */ zcrypt_key_hold(wrappingkey, FTAG); txgkey = zcrypt_key_gen(crypt); VERIFY(zcrypt_wrap_key(wrappingkey, txgkey, &wkeybuf, &wkeylen, zio_crypt_select_wrap(crypt)) == 0); zcrypt_key_release(wrappingkey, FTAG); dsl_keychain_set_key(dd, tx, wkeybuf, wkeylen, dsphys->ds_creation_time); kmem_free(wkeybuf, wkeylen); zcrypt_key_free(txgkey); spa_history_log_internal(spa, "key create", tx, "rekey succeeded dataset = %llu from dataset = %llu", dsobj, clone_origin->ds_object); } else { spa_history_log_internal(spa, "key create", tx, "succeeded dataset = %llu from dataset = %llu", dsobj, clone_origin->ds_object); } if (wrappingkey != NULL) { error = dsl_keychain_load_dd(dd, dsobj, crypt, wrappingkey); } return (error); }