summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/fs/zfs
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/fs/zfs')
-rw-r--r--usr/src/uts/common/fs/zfs/dmu_send.c75
-rw-r--r--usr/src/uts/common/fs/zfs/dsl_bookmark.c40
-rw-r--r--usr/src/uts/common/fs/zfs/dsl_crypt.c103
-rw-r--r--usr/src/uts/common/fs/zfs/dsl_dataset.c21
-rw-r--r--usr/src/uts/common/fs/zfs/spa.c10
-rw-r--r--usr/src/uts/common/fs/zfs/sys/dmu_send.h2
-rw-r--r--usr/src/uts/common/fs/zfs/sys/dsl_crypt.h5
-rw-r--r--usr/src/uts/common/fs/zfs/sys/dsl_dataset.h6
-rw-r--r--usr/src/uts/common/fs/zfs/zcp_get.c5
9 files changed, 232 insertions, 35 deletions
diff --git a/usr/src/uts/common/fs/zfs/dmu_send.c b/usr/src/uts/common/fs/zfs/dmu_send.c
index ef3c43b87d..db5e2e01ef 100644
--- a/usr/src/uts/common/fs/zfs/dmu_send.c
+++ b/usr/src/uts/common/fs/zfs/dmu_send.c
@@ -1073,9 +1073,13 @@ dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *to_ds,
}
if (featureflags & DMU_BACKUP_FEATURE_RAW) {
+ uint64_t ivset_guid = (ancestor_zb != NULL) ?
+ ancestor_zb->zbm_ivset_guid : 0;
+
ASSERT(os->os_encrypted);
- err = dsl_crypto_populate_key_nvlist(to_ds, &keynvl);
+ err = dsl_crypto_populate_key_nvlist(to_ds,
+ ivset_guid, &keynvl);
if (err != 0) {
fnvlist_free(nvl);
goto out;
@@ -1189,7 +1193,7 @@ dmu_send_obj(const char *pool, uint64_t tosnap, uint64_t fromsnap,
}
if (fromsnap != 0) {
- zfs_bookmark_phys_t zb;
+ zfs_bookmark_phys_t zb = { 0 };
boolean_t is_clone;
err = dsl_dataset_hold_obj(dp, fromsnap, FTAG, &fromds);
@@ -1198,12 +1202,25 @@ dmu_send_obj(const char *pool, uint64_t tosnap, uint64_t fromsnap,
dsl_pool_rele(dp, FTAG);
return (err);
}
- if (!dsl_dataset_is_before(ds, fromds, 0))
+ if (!dsl_dataset_is_before(ds, fromds, 0)) {
err = SET_ERROR(EXDEV);
+ dsl_dataset_rele(fromds, FTAG);
+ dsl_dataset_rele_flags(ds, dsflags, FTAG);
+ dsl_pool_rele(dp, FTAG);
+ return (err);
+ }
+
zb.zbm_creation_time =
dsl_dataset_phys(fromds)->ds_creation_time;
zb.zbm_creation_txg = dsl_dataset_phys(fromds)->ds_creation_txg;
zb.zbm_guid = dsl_dataset_phys(fromds)->ds_guid;
+
+ if (dsl_dataset_is_zapified(fromds)) {
+ (void) zap_lookup(dp->dp_meta_objset,
+ fromds->ds_object, DS_FIELD_IVSET_GUID, 8, 1,
+ &zb.zbm_ivset_guid);
+ }
+
is_clone = (fromds->ds_dir != ds->ds_dir);
dsl_dataset_rele(fromds, FTAG);
err = dmu_send_impl(FTAG, dp, ds, &zb, is_clone,
@@ -1253,7 +1270,7 @@ dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok,
}
if (fromsnap != NULL) {
- zfs_bookmark_phys_t zb;
+ zfs_bookmark_phys_t zb = { 0 };
boolean_t is_clone = B_FALSE;
int fsnamelen = strchr(tosnap, '@') - tosnap;
@@ -1279,6 +1296,13 @@ dmu_send(const char *tosnap, const char *fromsnap, boolean_t embedok,
dsl_dataset_phys(fromds)->ds_creation_txg;
zb.zbm_guid = dsl_dataset_phys(fromds)->ds_guid;
is_clone = (ds->ds_dir != fromds->ds_dir);
+
+ if (dsl_dataset_is_zapified(fromds)) {
+ (void) zap_lookup(dp->dp_meta_objset,
+ fromds->ds_object,
+ DS_FIELD_IVSET_GUID, 8, 1,
+ &zb.zbm_ivset_guid);
+ }
dsl_dataset_rele(fromds, FTAG);
}
} else {
@@ -1476,7 +1500,6 @@ typedef struct dmu_recv_begin_arg {
dmu_recv_cookie_t *drba_cookie;
cred_t *drba_cred;
dsl_crypto_params_t *drba_dcp;
- uint64_t drba_snapobj;
} dmu_recv_begin_arg_t;
static int
@@ -1524,7 +1547,7 @@ recv_begin_check_existing_impl(dmu_recv_begin_arg_t *drba, dsl_dataset_t *ds,
dsl_dataset_t *snap;
uint64_t obj = dsl_dataset_phys(ds)->ds_prev_snap_obj;
- /* Can't perform a raw receive on top of a non-raw receive */
+ /* Can't raw receive on top of an unencrypted dataset */
if (!encrypted && raw)
return (SET_ERROR(EINVAL));
@@ -1551,7 +1574,7 @@ recv_begin_check_existing_impl(dmu_recv_begin_arg_t *drba, dsl_dataset_t *ds,
return (SET_ERROR(ENODEV));
if (drba->drba_cookie->drc_force) {
- drba->drba_snapobj = obj;
+ drba->drba_cookie->drc_fromsnapobj = obj;
} else {
/*
* If we are not forcing, there must be no
@@ -1561,7 +1584,8 @@ recv_begin_check_existing_impl(dmu_recv_begin_arg_t *drba, dsl_dataset_t *ds,
dsl_dataset_rele(snap, FTAG);
return (SET_ERROR(ETXTBSY));
}
- drba->drba_snapobj = ds->ds_prev->ds_object;
+ drba->drba_cookie->drc_fromsnapobj =
+ ds->ds_prev->ds_object;
}
dsl_dataset_rele(snap, FTAG);
@@ -1596,7 +1620,7 @@ recv_begin_check_existing_impl(dmu_recv_begin_arg_t *drba, dsl_dataset_t *ds,
return (SET_ERROR(EINVAL));
}
- drba->drba_snapobj = 0;
+ drba->drba_cookie->drc_fromsnapobj = 0;
}
return (0);
@@ -1820,7 +1844,7 @@ dmu_recv_begin_sync(void *arg, dmu_tx_t *tx)
* the raw cmd set. Raw incremental recvs do not use a dcp
* since the encryption parameters are already set in stone.
*/
- if (dcp == NULL && drba->drba_snapobj == 0 &&
+ if (dcp == NULL && drba->drba_cookie->drc_fromsnapobj == 0 &&
drba->drba_origin == NULL) {
ASSERT3P(dcp, ==, NULL);
dcp = &dummy_dcp;
@@ -1834,15 +1858,15 @@ dmu_recv_begin_sync(void *arg, dmu_tx_t *tx)
/* create temporary clone */
dsl_dataset_t *snap = NULL;
- if (drba->drba_snapobj != 0) {
+ if (drba->drba_cookie->drc_fromsnapobj != 0) {
VERIFY0(dsl_dataset_hold_obj(dp,
- drba->drba_snapobj, FTAG, &snap));
+ drba->drba_cookie->drc_fromsnapobj, FTAG, &snap));
ASSERT3P(dcp, ==, NULL);
}
dsobj = dsl_dataset_create_sync(ds->ds_dir, recv_clone_name,
snap, crflags, drba->drba_cred, dcp, tx);
- if (drba->drba_snapobj != 0)
+ if (drba->drba_cookie->drc_fromsnapobj != 0)
dsl_dataset_rele(snap, FTAG);
dsl_dataset_rele_flags(ds, dsflags, FTAG);
} else {
@@ -3734,11 +3758,15 @@ dmu_recv_stream(dmu_recv_cookie_t *drc, vnode_t *vp, offset_t *voffp,
* the keynvl away until then.
*/
err = dsl_crypto_recv_raw(spa_name(ra.os->os_spa),
- drc->drc_ds->ds_object, drc->drc_drrb->drr_type,
- keynvl, drc->drc_newfs);
+ drc->drc_ds->ds_object, drc->drc_fromsnapobj,
+ drc->drc_drrb->drr_type, keynvl, drc->drc_newfs);
if (err != 0)
goto out;
+ /* see comment in dmu_recv_end_sync() */
+ (void) nvlist_lookup_uint64(keynvl, "to_ivset_guid",
+ &drc->drc_ivset_guid);
+
if (!drc->drc_newfs)
drc->drc_keynvl = fnvlist_dup(keynvl);
}
@@ -4054,6 +4082,23 @@ dmu_recv_end_sync(void *arg, dmu_tx_t *tx)
}
/*
+ * If this is a raw receive, the crypt_keydata nvlist will include
+ * a to_ivset_guid for us to set on the new snapshot. This value
+ * will override the value generated by the snapshot code. Older
+ * implementations of the raw send code did not include this
+ * value. By setting the zfs_disable_ivset_guid_check tunable, the
+ * user can still receive these streams, using the generated value
+ * if one hasn't been provided.
+ */
+ if (drc->drc_raw && drc->drc_ivset_guid != 0) {
+ dmu_object_zapify(dp->dp_meta_objset, drc->drc_newsnapobj,
+ DMU_OT_DSL_DATASET, tx);
+ VERIFY0(zap_update(dp->dp_meta_objset, drc->drc_newsnapobj,
+ DS_FIELD_IVSET_GUID, sizeof (uint64_t), 1,
+ &drc->drc_ivset_guid, tx));
+ }
+
+ /*
* Release the hold from dmu_recv_begin. This must be done before
* we return to open context, so that when we free the dataset's dnode
* we can evict its bonus buffer. Since the dataset may be destroyed
diff --git a/usr/src/uts/common/fs/zfs/dsl_bookmark.c b/usr/src/uts/common/fs/zfs/dsl_bookmark.c
index 08e8355414..a32198402f 100644
--- a/usr/src/uts/common/fs/zfs/dsl_bookmark.c
+++ b/usr/src/uts/common/fs/zfs/dsl_bookmark.c
@@ -221,6 +221,26 @@ dsl_bookmark_create_sync(void *arg, dmu_tx_t *tx)
bmark_phys.zbm_creation_time =
dsl_dataset_phys(snapds)->ds_creation_time;
+ /*
+ * If the dataset is encrypted create a larger bookmark to
+ * accommodate the IVset guid. The IVset guid was added
+ * after the encryption feature to prevent a problem with
+ * raw sends. If we encounter an encrypted dataset without
+ * an IVset guid we fall back to a normal bookmark.
+ */
+ if (snapds->ds_dir->dd_crypto_obj != 0 &&
+ spa_feature_is_enabled(dp->dp_spa,
+ SPA_FEATURE_BOOKMARK_V2)) {
+ int err = zap_lookup(mos, snapds->ds_object,
+ DS_FIELD_IVSET_GUID, sizeof (uint64_t), 1,
+ &bmark_phys.zbm_ivset_guid);
+ if (err == 0) {
+ bmark_len = BOOKMARK_PHYS_SIZE_V2;
+ spa_feature_incr(dp->dp_spa,
+ SPA_FEATURE_BOOKMARK_V2, tx);
+ }
+ }
+
VERIFY0(zap_add(mos, bmark_fs->ds_bookmarks,
shortname, sizeof (uint64_t),
bmark_len / sizeof (uint64_t), &bmark_phys, tx));
@@ -273,7 +293,7 @@ dsl_get_bookmarks_impl(dsl_dataset_t *ds, nvlist_t *props, nvlist_t *outnvl)
zap_cursor_retrieve(&zc, &attr) == 0;
zap_cursor_advance(&zc)) {
char *bmark_name = attr.za_name;
- zfs_bookmark_phys_t bmark_phys;
+ zfs_bookmark_phys_t bmark_phys = { 0 };
err = dsl_dataset_bmark_lookup(ds, bmark_name, &bmark_phys);
ASSERT3U(err, !=, ENOENT);
@@ -296,6 +316,11 @@ dsl_get_bookmarks_impl(dsl_dataset_t *ds, nvlist_t *props, nvlist_t *outnvl)
dsl_prop_nvlist_add_uint64(out_props,
ZFS_PROP_CREATION, bmark_phys.zbm_creation_time);
}
+ if (nvlist_exists(props,
+ zfs_prop_to_name(ZFS_PROP_IVSET_GUID))) {
+ dsl_prop_nvlist_add_uint64(out_props,
+ ZFS_PROP_IVSET_GUID, bmark_phys.zbm_ivset_guid);
+ }
fnvlist_add_nvlist(outnvl, bmark_name, out_props);
fnvlist_free(out_props);
@@ -343,13 +368,26 @@ typedef struct dsl_bookmark_destroy_arg {
static int
dsl_dataset_bookmark_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx)
{
+ int err;
objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
uint64_t bmark_zapobj = ds->ds_bookmarks;
matchtype_t mt = 0;
+ uint64_t int_size, num_ints;
if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET)
mt = MT_NORMALIZE;
+ err = zap_length(mos, bmark_zapobj, name, &int_size, &num_ints);
+ if (err != 0)
+ return (err);
+
+ ASSERT3U(int_size, ==, sizeof (uint64_t));
+
+ if (num_ints * int_size > BOOKMARK_PHYS_SIZE_V1) {
+ spa_feature_decr(dmu_objset_spa(mos),
+ SPA_FEATURE_BOOKMARK_V2, tx);
+ }
+
return (zap_remove_norm(mos, bmark_zapobj, name, mt, tx));
}
diff --git a/usr/src/uts/common/fs/zfs/dsl_crypt.c b/usr/src/uts/common/fs/zfs/dsl_crypt.c
index 6ef478e956..a0544a7ad2 100644
--- a/usr/src/uts/common/fs/zfs/dsl_crypt.c
+++ b/usr/src/uts/common/fs/zfs/dsl_crypt.c
@@ -71,6 +71,13 @@
* object is also refcounted.
*/
+/*
+ * This tunable allows datasets to be raw received even if the stream does
+ * not include IVset guids or if the guids don't match. This is used as part
+ * of the resolution for ZPOOL_ERRATA_ZOL_8308_ENCRYPTION.
+ */
+int zfs_disable_ivset_guid_check = 0;
+
static void
dsl_wrapping_key_hold(dsl_wrapping_key_t *wkey, void *tag)
{
@@ -1941,21 +1948,23 @@ dsl_dataset_create_crypt_sync(uint64_t dsobj, dsl_dir_t *dd,
typedef struct dsl_crypto_recv_key_arg {
uint64_t dcrka_dsobj;
+ uint64_t dcrka_fromobj;
dmu_objset_type_t dcrka_ostype;
nvlist_t *dcrka_nvl;
boolean_t dcrka_do_key;
} dsl_crypto_recv_key_arg_t;
static int
-dsl_crypto_recv_raw_objset_check(dsl_dataset_t *ds, dmu_objset_type_t ostype,
- nvlist_t *nvl, dmu_tx_t *tx)
+dsl_crypto_recv_raw_objset_check(dsl_dataset_t *ds, dsl_dataset_t *fromds,
+ dmu_objset_type_t ostype, nvlist_t *nvl, dmu_tx_t *tx)
{
int ret;
objset_t *os;
dnode_t *mdn;
uint8_t *buf = NULL;
uint_t len;
- uint64_t intval, nlevels, blksz, ibs, nblkptr, maxblkid;
+ uint64_t intval, nlevels, blksz, ibs;
+ uint64_t nblkptr, maxblkid, from_ivset_guid = 0;
if (ostype != DMU_OST_ZFS && ostype != DMU_OST_ZVOL)
return (SET_ERROR(EINVAL));
@@ -2021,6 +2030,29 @@ dsl_crypto_recv_raw_objset_check(dsl_dataset_t *ds, dmu_objset_type_t ostype,
}
rrw_exit(&ds->ds_bp_rwlock, FTAG);
+ /*
+ * Check that the ivset guid of the fromds matches the one from the
+ * send stream. Older versions of the encryption code did not have
+ * an ivset guid on the from dataset and did not send one in the
+ * stream. For these streams we provide the
+ * zfs_disable_ivset_guid_check tunable to allow these datasets to
+ * be received with a generated ivset guid.
+ */
+ if (fromds != NULL) {
+ intval = 0;
+ (void) nvlist_lookup_uint64(nvl, "from_ivset_guid", &intval);
+ (void) zap_lookup(tx->tx_pool->dp_meta_objset,
+ fromds->ds_object, DS_FIELD_IVSET_GUID,
+ sizeof (from_ivset_guid), 1, &from_ivset_guid);
+
+ if (!zfs_disable_ivset_guid_check &&
+ (intval == 0 || from_ivset_guid == 0))
+ return (SET_ERROR(ZFS_ERR_FROM_IVSET_GUID_MISSING));
+
+ if (!zfs_disable_ivset_guid_check && intval != from_ivset_guid)
+ return (SET_ERROR(ZFS_ERR_FROM_IVSET_GUID_MISMATCH));
+ }
+
return (0);
}
@@ -2040,7 +2072,11 @@ dsl_crypto_recv_raw_objset_sync(dsl_dataset_t *ds, dmu_objset_type_t ostype,
VERIFY0(dmu_objset_from_ds(ds, &os));
mdn = DMU_META_DNODE(os);
- /* fetch the values we need from the nvlist */
+ /*
+ * Fetch the values we need from the nvlist. "to_ivset_guid" must
+ * be set on the snapshot, which doesn't exist yet. The receive
+ * code will take care of this for us later.
+ */
compress = fnvlist_lookup_uint64(nvl, "mdn_compress");
checksum = fnvlist_lookup_uint64(nvl, "mdn_checksum");
nlevels = fnvlist_lookup_uint64(nvl, "mdn_nlevels");
@@ -2104,7 +2140,7 @@ dsl_crypto_recv_raw_key_check(dsl_dataset_t *ds, nvlist_t *nvl, dmu_tx_t *tx)
objset_t *mos = tx->tx_pool->dp_meta_objset;
uint8_t *buf = NULL;
uint_t len;
- uint64_t intval, guid, version;
+ uint64_t intval, key_guid, version;
boolean_t is_passphrase = B_FALSE;
ASSERT(dsl_dataset_phys(ds)->ds_flags & DS_FLAG_INCONSISTENT);
@@ -2129,10 +2165,10 @@ dsl_crypto_recv_raw_key_check(dsl_dataset_t *ds, nvlist_t *nvl, dmu_tx_t *tx)
*/
if (ds->ds_dir->dd_crypto_obj != 0) {
ret = zap_lookup(mos, ds->ds_dir->dd_crypto_obj,
- DSL_CRYPTO_KEY_GUID, 8, 1, &guid);
+ DSL_CRYPTO_KEY_GUID, 8, 1, &key_guid);
if (ret != 0)
return (ret);
- if (intval != guid)
+ if (intval != key_guid)
return (SET_ERROR(EACCES));
}
@@ -2198,13 +2234,13 @@ dsl_crypto_recv_raw_key_sync(dsl_dataset_t *ds, nvlist_t *nvl, dmu_tx_t *tx)
uint_t len;
uint64_t rddobj, one = 1;
uint8_t *keydata, *hmac_keydata, *iv, *mac;
- uint64_t crypt, guid, keyformat, iters, salt;
+ uint64_t crypt, key_guid, keyformat, iters, salt;
uint64_t version = ZIO_CRYPT_KEY_CURRENT_VERSION;
char *keylocation = "prompt";
/* lookup the values we need to create the DSL Crypto Key */
crypt = fnvlist_lookup_uint64(nvl, DSL_CRYPTO_KEY_CRYPTO_SUITE);
- guid = fnvlist_lookup_uint64(nvl, DSL_CRYPTO_KEY_GUID);
+ key_guid = fnvlist_lookup_uint64(nvl, DSL_CRYPTO_KEY_GUID);
keyformat = fnvlist_lookup_uint64(nvl,
zfs_prop_to_name(ZFS_PROP_KEYFORMAT));
iters = fnvlist_lookup_uint64(nvl,
@@ -2259,7 +2295,7 @@ dsl_crypto_recv_raw_key_sync(dsl_dataset_t *ds, nvlist_t *nvl, dmu_tx_t *tx)
/* sync the key data to the ZAP object on disk */
dsl_crypto_key_sync_impl(mos, dd->dd_crypto_obj, crypt,
- rddobj, guid, iv, mac, keydata, hmac_keydata, keyformat, salt,
+ rddobj, key_guid, iv, mac, keydata, hmac_keydata, keyformat, salt,
iters, tx);
}
@@ -2268,14 +2304,21 @@ dsl_crypto_recv_key_check(void *arg, dmu_tx_t *tx)
{
int ret;
dsl_crypto_recv_key_arg_t *dcrka = arg;
- dsl_dataset_t *ds = NULL;
+ dsl_dataset_t *ds = NULL, *fromds = NULL;
ret = dsl_dataset_hold_obj(tx->tx_pool, dcrka->dcrka_dsobj,
FTAG, &ds);
if (ret != 0)
goto error;
- ret = dsl_crypto_recv_raw_objset_check(ds,
+ if (dcrka->dcrka_fromobj != 0) {
+ ret = dsl_dataset_hold_obj(tx->tx_pool, dcrka->dcrka_fromobj,
+ FTAG, &fromds);
+ if (ret != 0)
+ goto error;
+ }
+
+ ret = dsl_crypto_recv_raw_objset_check(ds, fromds,
dcrka->dcrka_ostype, dcrka->dcrka_nvl, tx);
if (ret != 0)
goto error;
@@ -2290,11 +2333,16 @@ dsl_crypto_recv_key_check(void *arg, dmu_tx_t *tx)
goto error;
dsl_dataset_rele(ds, FTAG);
+ if (fromds != NULL)
+ dsl_dataset_rele(fromds, FTAG);
+
return (0);
error:
if (ds != NULL)
dsl_dataset_rele(ds, FTAG);
+ if (fromds != NULL)
+ dsl_dataset_rele(fromds, FTAG);
return (ret);
}
@@ -2319,12 +2367,13 @@ dsl_crypto_recv_key_sync(void *arg, dmu_tx_t *tx)
* without wrapping it.
*/
int
-dsl_crypto_recv_raw(const char *poolname, uint64_t dsobj,
+dsl_crypto_recv_raw(const char *poolname, uint64_t dsobj, uint64_t fromobj,
dmu_objset_type_t ostype, nvlist_t *nvl, boolean_t do_key)
{
dsl_crypto_recv_key_arg_t dcrka;
dcrka.dcrka_dsobj = dsobj;
+ dcrka.dcrka_fromobj = fromobj;
dcrka.dcrka_ostype = ostype;
dcrka.dcrka_nvl = nvl;
dcrka.dcrka_do_key = do_key;
@@ -2334,7 +2383,8 @@ dsl_crypto_recv_raw(const char *poolname, uint64_t dsobj,
}
int
-dsl_crypto_populate_key_nvlist(dsl_dataset_t *ds, nvlist_t **nvl_out)
+dsl_crypto_populate_key_nvlist(dsl_dataset_t *ds, uint64_t from_ivset_guid,
+ nvlist_t **nvl_out)
{
int ret;
objset_t *os;
@@ -2345,8 +2395,9 @@ dsl_crypto_populate_key_nvlist(dsl_dataset_t *ds, nvlist_t **nvl_out)
dsl_dir_t *rdd = NULL;
dsl_pool_t *dp = ds->ds_dir->dd_pool;
objset_t *mos = dp->dp_meta_objset;
- uint64_t crypt = 0, guid = 0, format = 0;
+ uint64_t crypt = 0, key_guid = 0, format = 0;
uint64_t iters = 0, salt = 0, version = 0;
+ uint64_t to_ivset_guid = 0;
uint8_t raw_keydata[MASTER_KEY_MAX_LEN];
uint8_t raw_hmac_keydata[SHA512_HMAC_KEYLEN];
uint8_t iv[WRAPPING_IV_LEN];
@@ -2367,7 +2418,7 @@ dsl_crypto_populate_key_nvlist(dsl_dataset_t *ds, nvlist_t **nvl_out)
if (ret != 0)
goto error;
- ret = zap_lookup(mos, dckobj, DSL_CRYPTO_KEY_GUID, 8, 1, &guid);
+ ret = zap_lookup(mos, dckobj, DSL_CRYPTO_KEY_GUID, 8, 1, &key_guid);
if (ret != 0)
goto error;
@@ -2391,6 +2442,12 @@ dsl_crypto_populate_key_nvlist(dsl_dataset_t *ds, nvlist_t **nvl_out)
if (ret != 0)
goto error;
+ /* see zfs_disable_ivset_guid_check tunable for errata info */
+ ret = zap_lookup(mos, ds->ds_object, DS_FIELD_IVSET_GUID, 8, 1,
+ &to_ivset_guid);
+ if (ret != 0)
+ ASSERT3U(dp->dp_spa->spa_errata, !=, 0);
+
/*
* We don't support raw sends of legacy on-disk formats. See the
* comment in dsl_crypto_recv_key_check() for details.
@@ -2440,7 +2497,7 @@ dsl_crypto_populate_key_nvlist(dsl_dataset_t *ds, nvlist_t **nvl_out)
dsl_pool_config_exit(dp, FTAG);
fnvlist_add_uint64(nvl, DSL_CRYPTO_KEY_CRYPTO_SUITE, crypt);
- fnvlist_add_uint64(nvl, DSL_CRYPTO_KEY_GUID, guid);
+ fnvlist_add_uint64(nvl, DSL_CRYPTO_KEY_GUID, key_guid);
fnvlist_add_uint64(nvl, DSL_CRYPTO_KEY_VERSION, version);
VERIFY0(nvlist_add_uint8_array(nvl, DSL_CRYPTO_KEY_MASTER_KEY,
raw_keydata, MASTER_KEY_MAX_LEN));
@@ -2462,6 +2519,8 @@ dsl_crypto_populate_key_nvlist(dsl_dataset_t *ds, nvlist_t **nvl_out)
fnvlist_add_uint64(nvl, "mdn_indblkshift", mdn->dn_indblkshift);
fnvlist_add_uint64(nvl, "mdn_nblkptr", mdn->dn_nblkptr);
fnvlist_add_uint64(nvl, "mdn_maxblkid", mdn->dn_maxblkid);
+ fnvlist_add_uint64(nvl, "to_ivset_guid", to_ivset_guid);
+ fnvlist_add_uint64(nvl, "from_ivset_guid", from_ivset_guid);
*nvl_out = nvl;
return (0);
@@ -2572,6 +2631,10 @@ dsl_dataset_crypt_stats(dsl_dataset_t *ds, nvlist_t *nv)
zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), 8, 1, &intval) == 0) {
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_PBKDF2_ITERS, intval);
}
+ if (zap_lookup(dd->dd_pool->dp_meta_objset, ds->ds_object,
+ DS_FIELD_IVSET_GUID, 8, 1, &intval) == 0) {
+ dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_IVSET_GUID, intval);
+ }
if (dsl_dir_get_encryption_root_ddobj(dd, &intval) == 0) {
VERIFY0(dsl_dir_hold_obj(dd->dd_pool, intval, NULL, FTAG,
@@ -2807,3 +2870,9 @@ error:
return (ret);
}
+
+#if defined(_KERNEL)
+module_param(zfs_disable_ivset_guid_check, int, 0644);
+MODULE_PARM_DESC(zfs_disable_ivset_guid_check,
+ "Set to allow raw receives without IVset guids");
+#endif
diff --git a/usr/src/uts/common/fs/zfs/dsl_dataset.c b/usr/src/uts/common/fs/zfs/dsl_dataset.c
index e9fdaef927..8786a849e4 100644
--- a/usr/src/uts/common/fs/zfs/dsl_dataset.c
+++ b/usr/src/uts/common/fs/zfs/dsl_dataset.c
@@ -540,6 +540,13 @@ dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag,
ds->ds_reserved = ds->ds_quota = 0;
}
+ if (err == 0 && ds->ds_dir->dd_crypto_obj != 0 &&
+ ds->ds_is_snapshot &&
+ zap_contains(mos, dsobj, DS_FIELD_IVSET_GUID) != 0) {
+ dp->dp_spa->spa_errata =
+ ZPOOL_ERRATA_ZOL_8308_ENCRYPTION;
+ }
+
dsl_deadlist_open(&ds->ds_deadlist,
mos, dsl_dataset_phys(ds)->ds_deadlist_obj);
uint64_t remap_deadlist_obj =
@@ -1548,6 +1555,20 @@ dsl_dataset_snapshot_sync_impl(dsl_dataset_t *ds, const char *snapname,
sizeof (remap_deadlist_obj), 1, &remap_deadlist_obj, tx));
}
+ /*
+ * Create a ivset guid for this snapshot if the dataset is
+ * encrypted. This may be overridden by a raw receive. We
+ * only do this if the bookmark_v2 feature is enabled.
+ */
+ if (ds->ds_dir->dd_crypto_obj != 0 &&
+ spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARK_V2)) {
+ uint64_t ivset_guid = unique_create();
+
+ dmu_object_zapify(mos, dsobj, DMU_OT_DSL_DATASET, tx);
+ VERIFY0(zap_add(mos, dsobj, DS_FIELD_IVSET_GUID,
+ sizeof (ivset_guid), 1, &ivset_guid, tx));
+ }
+
ASSERT3U(dsl_dataset_phys(ds)->ds_prev_snap_txg, <, tx->tx_txg);
dsl_dataset_phys(ds)->ds_prev_snap_obj = dsobj;
dsl_dataset_phys(ds)->ds_prev_snap_txg = crtxg;
diff --git a/usr/src/uts/common/fs/zfs/spa.c b/usr/src/uts/common/fs/zfs/spa.c
index 48a4b2167e..89afe2511d 100644
--- a/usr/src/uts/common/fs/zfs/spa.c
+++ b/usr/src/uts/common/fs/zfs/spa.c
@@ -3037,6 +3037,16 @@ spa_ld_check_features(spa_t *spa, boolean_t *missing_feat_writep)
return (spa_vdev_err(rvd, VDEV_AUX_CORRUPT_DATA, EIO));
}
+ /*
+ * Encryption was added before bookmark_v2, even though bookmark_v2
+ * is now a dependency. If this pool has encryption enabled without
+ * bookmark_v2, trigger an errata message.
+ */
+ if (spa_feature_is_enabled(spa, SPA_FEATURE_ENCRYPTION) &&
+ !spa_feature_is_enabled(spa, SPA_FEATURE_BOOKMARK_V2)) {
+ spa->spa_errata = ZPOOL_ERRATA_ZOL_8308_ENCRYPTION;
+ }
+
return (0);
}
diff --git a/usr/src/uts/common/fs/zfs/sys/dmu_send.h b/usr/src/uts/common/fs/zfs/sys/dmu_send.h
index 391d27f2f6..7d850fe4b1 100644
--- a/usr/src/uts/common/fs/zfs/sys/dmu_send.h
+++ b/usr/src/uts/common/fs/zfs/sys/dmu_send.h
@@ -67,7 +67,9 @@ typedef struct dmu_recv_cookie {
struct avl_tree *drc_guid_to_ds_map;
nvlist_t *drc_keynvl;
zio_cksum_t drc_cksum;
+ uint64_t drc_fromsnapobj;
uint64_t drc_newsnapobj;
+ uint64_t drc_ivset_guid;
void *drc_owner;
cred_t *drc_cred;
} dmu_recv_cookie_t;
diff --git a/usr/src/uts/common/fs/zfs/sys/dsl_crypt.h b/usr/src/uts/common/fs/zfs/sys/dsl_crypt.h
index ca448ead48..8f9e163582 100644
--- a/usr/src/uts/common/fs/zfs/sys/dsl_crypt.h
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_crypt.h
@@ -189,12 +189,13 @@ void key_mapping_rele(spa_t *spa, dsl_key_mapping_t *km, void *tag);
int spa_keystore_lookup_key(spa_t *spa, uint64_t dsobj, void *tag,
dsl_crypto_key_t **dck_out);
-int dsl_crypto_populate_key_nvlist(struct dsl_dataset *ds, nvlist_t **nvl_out);
+int dsl_crypto_populate_key_nvlist(struct dsl_dataset *ds,
+ uint64_t from_ivset_guid, nvlist_t **nvl_out);
int dsl_crypto_recv_raw_key_check(struct dsl_dataset *ds,
nvlist_t *nvl, dmu_tx_t *tx);
void dsl_crypto_recv_raw_key_sync(struct dsl_dataset *ds,
nvlist_t *nvl, dmu_tx_t *tx);
-int dsl_crypto_recv_raw(const char *poolname, uint64_t dsobj,
+int dsl_crypto_recv_raw(const char *poolname, uint64_t dsobj, uint64_t fromobj,
dmu_objset_type_t ostype, nvlist_t *nvl, boolean_t do_key);
int spa_keystore_change_key(const char *dsname, dsl_crypto_params_t *dcp);
diff --git a/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h b/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h
index 384d42c298..189376eefc 100644
--- a/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h
@@ -117,6 +117,12 @@ struct dsl_key_mapping;
#define DS_FIELD_REMAP_DEADLIST "com.delphix:remap_deadlist"
/*
+ * This field is set to the ivset guid for encrypted snapshots. This is used
+ * for validating raw receives.
+ */
+#define DS_FIELD_IVSET_GUID "com.datto:ivset_guid"
+
+/*
* DS_FLAG_CI_DATASET is set if the dataset contains a file system whose
* name lookups should be performed case-insensitively.
*/
diff --git a/usr/src/uts/common/fs/zfs/zcp_get.c b/usr/src/uts/common/fs/zfs/zcp_get.c
index 85e00a0f93..19d330257b 100644
--- a/usr/src/uts/common/fs/zfs/zcp_get.c
+++ b/usr/src/uts/common/fs/zfs/zcp_get.c
@@ -422,6 +422,11 @@ get_special_prop(lua_State *state, dsl_dataset_t *ds, const char *dsname,
case ZFS_PROP_INCONSISTENT:
numval = dsl_get_inconsistent(ds);
break;
+ case ZFS_PROP_IVSET_GUID:
+ error = zap_lookup(ds->ds_dir->dd_pool->dp_meta_objset,
+ ds->ds_object, DS_FIELD_IVSET_GUID, sizeof (numval),
+ 1, &numval);
+ break;
case ZFS_PROP_RECEIVE_RESUME_TOKEN: {
char *token = get_receive_resume_stats_impl(ds);
VERIFY3U(strlcpy(strval, token, ZAP_MAXVALUELEN), <,