diff --git a/include/libzfs.h b/include/libzfs.h index bb0178eea8..7828a27596 100644 --- a/include/libzfs.h +++ b/include/libzfs.h @@ -747,6 +747,9 @@ typedef struct recvflags { /* skip receive of snapshot holds */ boolean_t skipholds; + + /* mount the filesystem unless nomount is specified */ + boolean_t domount; } recvflags_t; extern int zfs_receive(libzfs_handle_t *, const char *, nvlist_t *, diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c index 33d3eb6a53..7026438828 100644 --- a/lib/libzfs/libzfs_sendrecv.c +++ b/lib/libzfs/libzfs_sendrecv.c @@ -3369,19 +3369,12 @@ created_before(libzfs_handle_t *hdl, avl_tree_t *avl, * sent datasets to their final locations in the dataset hierarchy. */ static int -recv_fix_encryption_hierarchy(libzfs_handle_t *hdl, const char *destname, +recv_fix_encryption_hierarchy(libzfs_handle_t *hdl, const char *top_zfs, nvlist_t *stream_nv, avl_tree_t *stream_avl) { int err; nvpair_t *fselem = NULL; nvlist_t *stream_fss; - char *cp; - char top_zfs[ZFS_MAX_DATASET_NAME_LEN]; - - (void) strcpy(top_zfs, destname); - cp = strrchr(top_zfs, '@'); - if (cp != NULL) - *cp = '\0'; VERIFY(0 == nvlist_lookup_nvlist(stream_nv, "fss", &stream_fss)); @@ -3408,7 +3401,7 @@ recv_fix_encryption_hierarchy(libzfs_handle_t *hdl, const char *destname, uint64_t guid; VERIFY(0 == nvpair_value_uint64(snapel, &guid)); - err = guid_to_name(hdl, destname, guid, B_FALSE, + err = guid_to_name(hdl, top_zfs, guid, B_FALSE, fsname); if (err == 0) break; @@ -4009,8 +4002,8 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname, stream_nv, stream_avl, NULL); } - if (raw && softerr == 0) { - softerr = recv_fix_encryption_hierarchy(hdl, destname, + if (raw && softerr == 0 && *top_zfs != NULL) { + softerr = recv_fix_encryption_hierarchy(hdl, *top_zfs, stream_nv, stream_avl); } @@ -4872,8 +4865,17 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, goto out; } - if (top_zfs && (*top_zfs == NULL || strcmp(*top_zfs, name) == 0)) + /* + * If this is the top-level dataset, record it so we can use it + * for recursive operations later. + */ + if (top_zfs != NULL && + (*top_zfs == NULL || strcmp(*top_zfs, name) == 0)) { toplevel = B_TRUE; + if (*top_zfs == NULL) + *top_zfs = zfs_strdup(hdl, name); + } + if (drrb->drr_type == DMU_OST_ZVOL) { type = ZFS_TYPE_VOLUME; } else if (drrb->drr_type == DMU_OST_ZFS) { @@ -5127,35 +5129,15 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, * children of the target filesystem if we did a replication * receive (indicated by stream_avl being non-NULL). */ - cp = strchr(destsnap, '@'); - if (cp && (ioctl_err == 0 || !newfs) && !redacted) { - zfs_handle_t *h; - - *cp = '\0'; - h = zfs_open(hdl, destsnap, - ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); - if (h != NULL) { - if (h->zfs_type == ZFS_TYPE_VOLUME) { - *cp = '@'; - } else if (newfs || stream_avl) { - /* - * Track the first/top of hierarchy fs, - * for mounting and sharing later. - */ - if (top_zfs && *top_zfs == NULL) - *top_zfs = zfs_strdup(hdl, destsnap); - } - zfs_close(h); - } - *cp = '@'; - } - if (clp) { if (!flags->nomount) err |= changelist_postfix(clp); changelist_free(clp); } + if ((newfs || stream_avl) && type == ZFS_TYPE_FILESYSTEM && !redacted) + flags->domount = B_TRUE; + if (prop_errflags & ZPROP_ERR_NOCLEAR) { (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Warning: " "failed to clear unreceived properties on %s"), name); @@ -5454,24 +5436,38 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, nvlist_t *props, VERIFY(0 == close(cleanup_fd)); - if (err == 0 && !flags->nomount && top_zfs) { + if (err == 0 && !flags->nomount && flags->domount && top_zfs) { zfs_handle_t *zhp = NULL; prop_changelist_t *clp = NULL; - zhp = zfs_open(hdl, top_zfs, ZFS_TYPE_FILESYSTEM); - if (zhp != NULL) { + zhp = zfs_open(hdl, top_zfs, + ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); + if (zhp == NULL) { + err = -1; + goto out; + } else { + if (zhp->zfs_type == ZFS_TYPE_VOLUME) { + zfs_close(zhp); + goto out; + } + clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, CL_GATHER_MOUNT_ALWAYS, 0); zfs_close(zhp); - if (clp != NULL) { - /* mount and share received datasets */ - err = changelist_postfix(clp); - changelist_free(clp); + if (clp == NULL) { + err = -1; + goto out; } + + /* mount and share received datasets */ + err = changelist_postfix(clp); + changelist_free(clp); + if (err != 0) + err = -1; } - if (zhp == NULL || clp == NULL || err) - err = -1; } + +out: if (top_zfs) free(top_zfs); diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run index 3e39c165ee..5bc810803c 100644 --- a/tests/runfiles/linux.run +++ b/tests/runfiles/linux.run @@ -212,7 +212,7 @@ tests = ['zfs_receive_001_pos', 'zfs_receive_002_pos', 'zfs_receive_003_pos', 'zfs_receive_013_pos', 'zfs_receive_014_pos', 'zfs_receive_015_pos', 'receive-o-x_props_override', 'zfs_receive_from_encrypted', 'zfs_receive_to_encrypted', 'zfs_receive_raw', - 'zfs_receive_raw_incremental', 'zfs_receive_-e'] + 'zfs_receive_raw_incremental', 'zfs_receive_-e', 'zfs_receive_raw_-d'] tags = ['functional', 'cli_root', 'zfs_receive'] [tests/functional/cli_root/zfs_rename] diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_receive/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/zfs_receive/Makefile.am index bf112a77e6..7b2037b9f2 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zfs_receive/Makefile.am +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_receive/Makefile.am @@ -22,4 +22,5 @@ dist_pkgdata_SCRIPTS = \ zfs_receive_to_encrypted.ksh \ zfs_receive_raw.ksh \ zfs_receive_raw_incremental.ksh \ + zfs_receive_raw_-d.ksh \ zfs_receive_-e.ksh diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_raw_-d.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_raw_-d.ksh new file mode 100755 index 0000000000..a909f2788b --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zfs_receive/zfs_receive_raw_-d.ksh @@ -0,0 +1,62 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# CDDL HEADER END +# + +# +# Copyright (c) 2019 Datto, Inc. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +# +# DESCRIPTION: +# zfs receive -d should create the expected encryption hierarchy. +# +# STRATEGY: +# 1. Create an encrypted dataset and a inheriting child +# 2. Snapshot the child dataset +# 2. Create a recursive raw send file from the snapshot +# 3. Destroy the original child filesystem +# 4. Receive the snapshot as a child of the second dataset with '-d' +# 5. Verify the new child can be mounted +# + +verify_runnable "both" + +function cleanup +{ + datasetexists $TESTPOOL/$TESTFS1 && \ + log_must zfs destroy -r $TESTPOOL/$TESTFS1 + rm -f $sendfile +} + +log_onexit cleanup + +log_assert "zfs receive -d should create the expected encryption hierarchy" + +typeset passphrase="password1" + +sendfile=$TEST_BASE_DIR/sendfile.$$ + +log_must eval "echo $passphrase | zfs create -o encryption=on" \ + "-o keyformat=passphrase $TESTPOOL/$TESTFS1" +log_must zfs create $TESTPOOL/$TESTFS1/child +log_must zfs snapshot $TESTPOOL/$TESTFS1/child@snap +log_must eval "zfs send -Rw $TESTPOOL/$TESTFS1/child@snap > $sendfile" +log_must zfs destroy -r $TESTPOOL/$TESTFS1/child +log_must zfs receive -Fd $TESTPOOL < $sendfile +log_must eval "echo $passphrase | zfs mount -l $TESTPOOL/$TESTFS1/child" + +log_pass "zfs receive -d creates the expected encryption hierarchy"