diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c index b9780720e5a3..c2c9bbff1047 100644 --- a/lib/libzfs/libzfs_sendrecv.c +++ b/lib/libzfs/libzfs_sendrecv.c @@ -3376,66 +3376,78 @@ created_before(libzfs_handle_t *hdl, avl_tree_t *avl, */ static int recv_fix_encryption_hierarchy(libzfs_handle_t *hdl, const char *top_zfs, - nvlist_t *stream_nv) + nvlist_t *stream_nv, avl_tree_t *stream_avl) { int err; nvpair_t *fselem = NULL; - nvlist_t *stream_fss; + nvlist_t *local_nv; + avl_tree_t *local_avl; + boolean_t recursive; + + recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") == + ENOENT); - stream_fss = fnvlist_lookup_nvlist(stream_nv, "fss"); + /* Using top_zfs, gather the nvlists for all local filesystems. */ + if ((err = gather_nvlist(hdl, top_zfs, NULL, NULL, + recursive, B_TRUE, B_FALSE, recursive, B_FALSE, B_FALSE, B_FALSE, + B_FALSE, B_TRUE, &local_nv, &local_avl)) != 0) + return (err); - while ((fselem = nvlist_next_nvpair(stream_fss, fselem)) != NULL) { + /* + * Go through the nvlists of the local filesystems and check for + * encryption roots. + */ + while ((fselem = nvlist_next_nvpair(local_nv, fselem)) != NULL) { zfs_handle_t *zhp = NULL; uint64_t crypt; - nvlist_t *snaps, *props, *stream_nvfs = NULL; - nvpair_t *snapel = NULL; + nvlist_t *stream_props, *snaps, *stream_nvfs = NULL, + *nvfs = NULL; boolean_t is_encroot, is_clone, stream_encroot; - char *cp; - const char *stream_keylocation = NULL; + const char *stream_keylocation = NULL, *fsname; char keylocation[MAXNAMELEN]; - char fsname[ZFS_MAX_DATASET_NAME_LEN]; - - keylocation[0] = '\0'; - stream_nvfs = fnvpair_value_nvlist(fselem); - snaps = fnvlist_lookup_nvlist(stream_nvfs, "snaps"); - props = fnvlist_lookup_nvlist(stream_nvfs, "props"); - stream_encroot = nvlist_exists(stream_nvfs, "is_encroot"); - - /* find a snapshot from the stream that exists locally */ - err = ENOENT; - while ((snapel = nvlist_next_nvpair(snaps, snapel)) != NULL) { - uint64_t guid; - - guid = fnvpair_value_uint64(snapel); - err = guid_to_name(hdl, top_zfs, guid, B_FALSE, - fsname); - if (err == 0) - break; - } - - if (err != 0) - continue; - - cp = strchr(fsname, '@'); - if (cp != NULL) - *cp = '\0'; + nvpair_t *snapelem; + nvfs = fnvpair_value_nvlist(fselem); + snaps = fnvlist_lookup_nvlist(nvfs, "snaps"); + fsname = fnvlist_lookup_string(nvfs, "name"); zhp = zfs_open(hdl, fsname, ZFS_TYPE_DATASET); if (zhp == NULL) { err = ENOENT; goto error; } - crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION); - is_clone = zhp->zfs_dmustats.dds_origin[0] != '\0'; - (void) zfs_crypto_get_encryption_root(zhp, &is_encroot, NULL); - /* we don't need to do anything for unencrypted datasets */ + crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION); if (crypt == ZIO_CRYPT_OFF) { zfs_close(zhp); continue; } + is_clone = zhp->zfs_dmustats.dds_origin[0] != '\0'; + (void) zfs_crypto_get_encryption_root(zhp, &is_encroot, NULL); + keylocation[0] = '\0'; + + /* + * Go through the snapshots of the local filesystem and find + * the stream's filesystem. + */ + for (snapelem = nvlist_next_nvpair(snaps, NULL); + snapelem; snapelem = nvlist_next_nvpair(snaps, snapelem)) { + uint64_t thisguid; + + thisguid = fnvpair_value_uint64(snapelem); + stream_nvfs = fsavl_find(stream_avl, thisguid, NULL); + + if (stream_nvfs != NULL) + break; + } + + if (stream_nvfs == NULL) + continue; + + stream_props = fnvlist_lookup_nvlist(stream_nvfs, "props"); + stream_encroot = nvlist_exists(stream_nvfs, "is_encroot"); + /* * If the dataset is flagged as an encryption root, was not * received as a clone and is not currently an encryption root, @@ -3451,7 +3463,7 @@ recv_fix_encryption_hierarchy(libzfs_handle_t *hdl, const char *top_zfs, } } - stream_keylocation = fnvlist_lookup_string(props, + stream_keylocation = fnvlist_lookup_string(stream_props, zfs_prop_to_name(ZFS_PROP_KEYLOCATION)); /* @@ -3518,14 +3530,14 @@ recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs, boolean_t needagain, progress, recursive; const char *s1, *s2; + if (flags->dryrun) + return (0); + fromsnap = fnvlist_lookup_string(stream_nv, "fromsnap"); recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") == ENOENT); - if (flags->dryrun) - return (0); - again: needagain = progress = B_FALSE; @@ -3999,9 +4011,9 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname, stream_nv, stream_avl, NULL); } - if (raw && softerr == 0 && *top_zfs != NULL) { + if (raw && softerr == 0 && *top_zfs != NULL && !flags->dryrun) { softerr = recv_fix_encryption_hierarchy(hdl, *top_zfs, - stream_nv); + stream_nv, stream_avl); } out: diff --git a/tests/zfs-tests/tests/functional/rsend/send_encrypted_hierarchy.ksh b/tests/zfs-tests/tests/functional/rsend/send_encrypted_hierarchy.ksh index 8417afc88d33..9a4f7ced8b45 100755 --- a/tests/zfs-tests/tests/functional/rsend/send_encrypted_hierarchy.ksh +++ b/tests/zfs-tests/tests/functional/rsend/send_encrypted_hierarchy.ksh @@ -48,49 +48,107 @@ log_onexit cleanup # Create the filesystem hierarchy log_must cleanup_pool $POOL +log_must zfs create $POOL/fs1 +log_must zfs create $POOL2/fs2 log_must eval "echo $PASSPHRASE | zfs create -o encryption=on" \ - "-o keyformat=passphrase $POOL/$FS" -log_must zfs snapshot $POOL/$FS@snap -log_must zfs clone $POOL/$FS@snap $POOL/clone -log_must zfs create $POOL/$FS/child + "-o keyformat=passphrase $POOL/fs1/$FS" +log_must zfs snapshot $POOL/fs1/$FS@snap +log_must zfs clone $POOL/fs1/$FS@snap $POOL/clone +log_must zfs create $POOL/fs1/$FS/child +log_must eval "echo $PASSPHRASE | zfs create -o encryption=on" \ + "-o keyformat=passphrase $POOL/fs1/a" +log_must zfs snapshot $POOL/fs1/a@snapa +log_must zfs clone $POOL/fs1/a@snapa $POOL/clonea +log_must zfs create $POOL/fs1/a/childa # Back up the tree and verify the structure log_must zfs snapshot -r $POOL@before log_must eval "zfs send -wR $POOL@before > $BACKDIR/fs-before-R" -log_must eval "zfs receive -d -F $POOL2 < $BACKDIR/fs-before-R" -dstds=$(get_dst_ds $POOL/$FS $POOL2) -log_must cmp_ds_subs $POOL/$FS $dstds - -log_must verify_encryption_root $POOL/$FS $POOL/$FS -log_must verify_keylocation $POOL/$FS "prompt" -log_must verify_origin $POOL/$FS "-" - -log_must verify_encryption_root $POOL/clone $POOL/$FS -log_must verify_keylocation $POOL/clone "none" -log_must verify_origin $POOL/clone "$POOL/$FS@snap" - -log_must verify_encryption_root $POOL/$FS/child $POOL/$FS -log_must verify_keylocation $POOL/$FS/child "none" +log_must eval "zfs receive -d -F $POOL2/fs2 < $BACKDIR/fs-before-R" +dstds=$(get_dst_ds $POOL/fs1/$FS $POOL2/fs2) +dstdsa=$(get_dst_ds $POOL/fs1/a $POOL2/fs2) +log_must cmp_ds_subs $POOL/fs1/$FS $dstds +log_must cmp_ds_subs $POOL/fs1/a $dstdsa + +log_must verify_encryption_root $POOL2/fs2/fs1/$FS $POOL2/fs2/fs1/$FS +log_must verify_keylocation $POOL2/fs2/fs1/$FS "prompt" +log_must verify_origin $POOL2/fs2/fs1/$FS "-" +log_must verify_encryption_root $POOL2/fs2/fs1/a $POOL2/fs2/fs1/a +log_must verify_keylocation $POOL2/fs2/fs1/a "prompt" +log_must verify_origin $POOL2/fs2/fs1/a "-" + +log_must verify_encryption_root $POOL2/fs2/clone $POOL2/fs2/fs1/$FS +log_must verify_keylocation $POOL2/fs2/clone "none" +log_must verify_origin $POOL2/fs2/clone "$POOL2/fs2/fs1/$FS@snap" +log_must verify_encryption_root $POOL2/fs2/clonea $POOL2/fs2/fs1/a +log_must verify_keylocation $POOL2/fs2/clonea "none" +log_must verify_origin $POOL2/fs2/clonea "$POOL2/fs2/fs1/a@snapa" + +log_must verify_encryption_root $POOL/fs1/$FS/child $POOL/fs1/$FS +log_must verify_encryption_root $POOL2/fs2/fs1/$FS/child $POOL2/fs2/fs1/$FS +log_must verify_keylocation $POOL2/fs2/fs1/$FS/child "none" +log_must verify_encryption_root $POOL2/fs2/fs1/a/childa $POOL2/fs2/fs1/a +log_must verify_keylocation $POOL2/fs2/fs1/a/childa "none" # Alter the hierarchy and re-send log_must eval "echo $PASSPHRASE1 | zfs change-key -o keyformat=passphrase" \ - "$POOL/$FS/child" + "$POOL/fs1/$FS/child" +log_must eval "echo $PASSPHRASE1 | zfs change-key -o keyformat=passphrase" \ + "$POOL/fs1/a/childa" log_must zfs promote $POOL/clone +log_must zfs promote $POOL/clonea log_must zfs snapshot -r $POOL@after log_must eval "zfs send -wR -i $POOL@before $POOL@after >" \ "$BACKDIR/fs-after-R" -log_must eval "zfs receive -d -F $POOL2 < $BACKDIR/fs-after-R" -log_must cmp_ds_subs $POOL/$FS $dstds +log_must eval "zfs receive -d -F $POOL2/fs2 < $BACKDIR/fs-after-R" +log_must cmp_ds_subs $POOL/fs1/$FS $dstds +log_must cmp_ds_subs $POOL/fs1/a $dstdsa -log_must verify_encryption_root $POOL/$FS $POOL/clone -log_must verify_keylocation $POOL/$FS "none" -log_must verify_origin $POOL/$FS "$POOL/clone@snap" +log_must verify_encryption_root $POOL/fs1/$FS $POOL/clone +log_must verify_keylocation $POOL/fs1/$FS "none" +log_must verify_origin $POOL/fs1/$FS "$POOL/clone@snap" +log_must verify_encryption_root $POOL/fs1/a $POOL/clonea +log_must verify_keylocation $POOL/fs1/a "none" +log_must verify_origin $POOL/fs1/a "$POOL/clonea@snapa" log_must verify_encryption_root $POOL/clone $POOL/clone log_must verify_keylocation $POOL/clone "prompt" log_must verify_origin $POOL/clone "-" - -log_must verify_encryption_root $POOL/$FS/child $POOL/$FS/child -log_must verify_keylocation $POOL/$FS/child "prompt" +log_must verify_encryption_root $POOL/clonea $POOL/clonea +log_must verify_keylocation $POOL/clonea "prompt" +log_must verify_origin $POOL/clonea "-" + +log_must verify_encryption_root $POOL/fs1/$FS/child $POOL/fs1/$FS/child +log_must verify_keylocation $POOL/fs1/$FS/child "prompt" +log_must verify_encryption_root $POOL/fs1/a/childa $POOL/fs1/a/childa +log_must verify_keylocation $POOL/fs1/a/childa "prompt" + +log_must verify_encryption_root $POOL2/fs2 "-" +log_must verify_encryption_root $POOL2/fs2/clone $POOL2/fs2/clone +log_must verify_encryption_root $POOL2/fs2/clonea $POOL2/fs2/clonea +log_must verify_encryption_root $POOL2/fs2/fs1 "-" +log_must verify_encryption_root $POOL2/fs2/fs1/a $POOL2/fs2/clonea +log_must verify_encryption_root $POOL2/fs2/fs1/a/childa $POOL2/fs2/fs1/a/childa +log_must verify_encryption_root $POOL2/fs2/fs1/$FS $POOL2/fs2/clone +log_must verify_encryption_root $POOL2/fs2/fs1/$FS/child $POOL2/fs2/fs1/$FS/child + +log_must verify_keylocation $POOL2/fs2 "none" +log_must verify_keylocation $POOL2/fs2/clone "prompt" +log_must verify_keylocation $POOL2/fs2/clonea "prompt" +log_must verify_keylocation $POOL2/fs2/fs1 "none" +log_must verify_keylocation $POOL2/fs2/fs1/a "none" +log_must verify_keylocation $POOL2/fs2/fs1/a/childa "prompt" +log_must verify_keylocation $POOL2/fs2/fs1/$FS "none" +log_must verify_keylocation $POOL2/fs2/fs1/$FS/child "prompt" + +log_must verify_origin $POOL2/fs2 "-" +log_must verify_origin $POOL2/fs2/clone "-" +log_must verify_origin $POOL2/fs2/clonea "-" +log_must verify_origin $POOL2/fs2/fs1 "-" +log_must verify_origin $POOL2/fs2/fs1/a "$POOL2/fs2/clonea@snapa" +log_must verify_origin $POOL2/fs2/fs1/a/childa "-" +log_must verify_origin $POOL2/fs2/fs1/$FS "$POOL2/fs2/clone@snap" +log_must verify_origin $POOL2/fs2/fs1/$FS/child "-" +log_must zfs list log_pass "Raw recursive sends preserve filesystem structure."