mirror of https://github.com/openzfs/zfs.git
Added encryption support for zfs recv -o / -x
One small integration that was absent from b52563 was support for zfs recv -o / -x with regards to encryption parameters. The main use cases of this are as follows: * Receiving an unencrypted stream as encrypted without needing to create a "dummy" encrypted parent so that encryption can be inheritted. * Allowing users to change their keylocation on receive, so long as the receiving dataset is an encryption root. * Allowing users to explicitly exclude or override the encryption property from an unencrypted properties stream, allowing it to be received as encrypted. * Receiving a recursive heirarchy of unencrypted datasets, encrypting the top-level one and forcing all children to inherit the encryption. Reviewed-by: Jorgen Lundman <lundman@lundman.net> Reviewed by: Matthew Ahrens <mahrens@delphix.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Richard Elling <Richard.Elling@RichardElling.com> Signed-off-by: Tom Caputi <tcaputi@datto.com> Closes #7650
This commit is contained in:
parent
fe8a7982ca
commit
d9c460a0b6
|
@ -1303,7 +1303,8 @@ def lzc_receive_one(
|
|||
@_uncommitted()
|
||||
def lzc_receive_with_cmdprops(
|
||||
snapname, fd, begin_record, force=False, resumable=False, raw=False,
|
||||
origin=None, props=None, cmdprops=None, cleanup_fd=-1, action_handle=0
|
||||
origin=None, props=None, cmdprops=None, key=None, cleanup_fd=-1,
|
||||
action_handle=0
|
||||
):
|
||||
'''
|
||||
Like :func:`lzc_receive_one`, but allows the caller to pass an additional
|
||||
|
@ -1333,6 +1334,8 @@ def lzc_receive_with_cmdprops(
|
|||
every other value is set locally as if the command "zfs set" was
|
||||
invoked immediately before the receive.
|
||||
:type cmdprops: dict of bytes : Any
|
||||
:param key: raw bytes representing user's wrapping key
|
||||
:type key: bytes
|
||||
:param int cleanup_fd: file descriptor used to set a cleanup-on-exit file
|
||||
descriptor.
|
||||
:param int action_handle: variable used to pass the handle for guid/ds
|
||||
|
@ -1400,14 +1403,19 @@ def lzc_receive_with_cmdprops(
|
|||
props = {}
|
||||
if cmdprops is None:
|
||||
cmdprops = {}
|
||||
if key is None:
|
||||
key = bytes("")
|
||||
else:
|
||||
key = bytes(key)
|
||||
|
||||
nvlist = nvlist_in(props)
|
||||
cmdnvlist = nvlist_in(cmdprops)
|
||||
properrs = {}
|
||||
with nvlist_out(properrs) as c_errors:
|
||||
ret = _lib.lzc_receive_with_cmdprops(
|
||||
snapname, nvlist, cmdnvlist, c_origin, force, resumable, raw, fd,
|
||||
begin_record, cleanup_fd, c_read_bytes, c_errflags,
|
||||
c_action_handle, c_errors)
|
||||
snapname, nvlist, cmdnvlist, key, len(key), c_origin,
|
||||
force, resumable, raw, fd, begin_record, cleanup_fd, c_read_bytes,
|
||||
c_errflags, c_action_handle, c_errors)
|
||||
errors.lzc_receive_translate_errors(
|
||||
ret, snapname, fd, force, raw, False, False, origin, properrs)
|
||||
return (int(c_read_bytes[0]), action_handle)
|
||||
|
|
|
@ -108,9 +108,9 @@ CDEF = """
|
|||
int lzc_receive_resumable(const char *, nvlist_t *, const char *,
|
||||
boolean_t, boolean_t, int);
|
||||
int lzc_receive_with_cmdprops(const char *, nvlist_t *, nvlist_t *,
|
||||
const char *, boolean_t, boolean_t, boolean_t, int,
|
||||
const dmu_replay_record_t *, int, uint64_t *, uint64_t *, uint64_t *,
|
||||
nvlist_t **);
|
||||
uint8_t *, uint_t, const char *, boolean_t, boolean_t,
|
||||
boolean_t, int, const dmu_replay_record_t *, int, uint64_t *,
|
||||
uint64_t *, uint64_t *, nvlist_t **);
|
||||
int lzc_receive_with_header(const char *, nvlist_t *, const char *,
|
||||
boolean_t, boolean_t, boolean_t, int, const dmu_replay_record_t *);
|
||||
int lzc_release(nvlist_t *, nvlist_t **);
|
||||
|
|
|
@ -527,7 +527,7 @@ extern nvlist_t *zfs_get_clones_nvl(zfs_handle_t *);
|
|||
*/
|
||||
extern int zfs_crypto_get_encryption_root(zfs_handle_t *, boolean_t *, char *);
|
||||
extern int zfs_crypto_create(libzfs_handle_t *, char *, nvlist_t *, nvlist_t *,
|
||||
uint8_t **, uint_t *);
|
||||
boolean_t stdin_available, uint8_t **, uint_t *);
|
||||
extern int zfs_crypto_clone_check(libzfs_handle_t *, zfs_handle_t *, char *,
|
||||
nvlist_t *);
|
||||
extern int zfs_crypto_attempt_load_keys(libzfs_handle_t *, char *);
|
||||
|
|
|
@ -93,7 +93,7 @@ int lzc_receive_one(const char *, nvlist_t *, const char *, boolean_t,
|
|||
boolean_t, boolean_t, int, const struct dmu_replay_record *, int,
|
||||
uint64_t *, uint64_t *, uint64_t *, nvlist_t **);
|
||||
int lzc_receive_with_cmdprops(const char *, nvlist_t *, nvlist_t *,
|
||||
const char *, boolean_t, boolean_t, boolean_t, int,
|
||||
uint8_t *, uint_t, const char *, boolean_t, boolean_t, boolean_t, int,
|
||||
const struct dmu_replay_record *, int, uint64_t *, uint64_t *,
|
||||
uint64_t *, nvlist_t **);
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#define _DMU_SEND_H
|
||||
|
||||
#include <sys/inttypes.h>
|
||||
#include <sys/dsl_crypt.h>
|
||||
#include <sys/spa.h>
|
||||
|
||||
struct vnode;
|
||||
|
@ -72,8 +73,9 @@ typedef struct dmu_recv_cookie {
|
|||
} dmu_recv_cookie_t;
|
||||
|
||||
int dmu_recv_begin(char *tofs, char *tosnap,
|
||||
struct dmu_replay_record *drr_begin,
|
||||
boolean_t force, boolean_t resumable, char *origin, dmu_recv_cookie_t *drc);
|
||||
struct dmu_replay_record *drr_begin, boolean_t force, boolean_t resumable,
|
||||
nvlist_t *localprops, nvlist_t *hidden_args, char *origin,
|
||||
dmu_recv_cookie_t *drc);
|
||||
int dmu_recv_stream(dmu_recv_cookie_t *drc, struct vnode *vp, offset_t *voffp,
|
||||
int cleanup_fd, uint64_t *action_handlep);
|
||||
int dmu_recv_end(dmu_recv_cookie_t *drc, void *owner);
|
||||
|
|
|
@ -667,7 +667,8 @@ zfs_crypto_get_encryption_root(zfs_handle_t *zhp, boolean_t *is_encroot,
|
|||
|
||||
int
|
||||
zfs_crypto_create(libzfs_handle_t *hdl, char *parent_name, nvlist_t *props,
|
||||
nvlist_t *pool_props, uint8_t **wkeydata_out, uint_t *wkeylen_out)
|
||||
nvlist_t *pool_props, boolean_t stdin_available, uint8_t **wkeydata_out,
|
||||
uint_t *wkeylen_out)
|
||||
{
|
||||
int ret;
|
||||
char errbuf[1024];
|
||||
|
@ -808,6 +809,17 @@ zfs_crypto_create(libzfs_handle_t *hdl, char *parent_name, nvlist_t *props,
|
|||
* encryption root. Populate the encryption params.
|
||||
*/
|
||||
if (keylocation != NULL) {
|
||||
/*
|
||||
* 'zfs recv -o keylocation=prompt' won't work because stdin
|
||||
* is being used by the send stream, so we disallow it.
|
||||
*/
|
||||
if (!stdin_available && strcmp(keylocation, "prompt") == 0) {
|
||||
ret = EINVAL;
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Cannot use "
|
||||
"'prompt' keylocation because stdin is in use."));
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = populate_create_encryption_params_nvlists(hdl, NULL,
|
||||
B_FALSE, keyformat, keylocation, props, &wkeydata,
|
||||
&wkeylen);
|
||||
|
|
|
@ -3731,8 +3731,8 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
|
|||
}
|
||||
|
||||
(void) parent_name(path, parent, sizeof (parent));
|
||||
if (zfs_crypto_create(hdl, parent, props, NULL, &wkeydata,
|
||||
&wkeylen) != 0) {
|
||||
if (zfs_crypto_create(hdl, parent, props, NULL, B_TRUE,
|
||||
&wkeydata, &wkeylen) != 0) {
|
||||
nvlist_free(props);
|
||||
return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));
|
||||
}
|
||||
|
|
|
@ -1224,7 +1224,7 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
|
|||
(nvlist_alloc(&zc_props, NV_UNIQUE_NAME, 0) != 0)) {
|
||||
goto create_failed;
|
||||
}
|
||||
if (zfs_crypto_create(hdl, NULL, zc_fsprops, props,
|
||||
if (zfs_crypto_create(hdl, NULL, zc_fsprops, props, B_TRUE,
|
||||
&wkeydata, &wkeylen) != 0) {
|
||||
zfs_error(hdl, EZFS_CRYPTOFAILED, msg);
|
||||
goto create_failed;
|
||||
|
|
|
@ -3461,16 +3461,19 @@ recv_ecksum_set_aux(libzfs_handle_t *hdl, const char *target_snap,
|
|||
* oxprops: valid output override (-o) and excluded (-x) properties
|
||||
*/
|
||||
static int
|
||||
zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type, boolean_t zoned,
|
||||
boolean_t recursive, boolean_t toplevel, nvlist_t *recvprops,
|
||||
nvlist_t *cmdprops, nvlist_t *origprops, nvlist_t **oxprops,
|
||||
const char *errbuf)
|
||||
zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type,
|
||||
char *fsname, boolean_t zoned, boolean_t recursive, boolean_t newfs,
|
||||
boolean_t raw, boolean_t toplevel, nvlist_t *recvprops, nvlist_t *cmdprops,
|
||||
nvlist_t *origprops, nvlist_t **oxprops, uint8_t **wkeydata_out,
|
||||
uint_t *wkeylen_out, const char *errbuf)
|
||||
{
|
||||
nvpair_t *nvp;
|
||||
nvlist_t *oprops, *voprops;
|
||||
zfs_handle_t *zhp = NULL;
|
||||
zpool_handle_t *zpool_hdl = NULL;
|
||||
char *cp;
|
||||
int ret = 0;
|
||||
char namebuf[ZFS_MAX_DATASET_NAME_LEN];
|
||||
|
||||
if (nvlist_empty(cmdprops))
|
||||
return (0); /* No properties to override or exclude */
|
||||
|
@ -3478,6 +3481,33 @@ zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type, boolean_t zoned,
|
|||
*oxprops = fnvlist_alloc();
|
||||
oprops = fnvlist_alloc();
|
||||
|
||||
strlcpy(namebuf, fsname, ZFS_MAX_DATASET_NAME_LEN);
|
||||
|
||||
/*
|
||||
* Get our dataset handle. The target dataset may not exist yet.
|
||||
*/
|
||||
if (zfs_dataset_exists(hdl, namebuf, ZFS_TYPE_DATASET)) {
|
||||
zhp = zfs_open(hdl, namebuf, ZFS_TYPE_DATASET);
|
||||
if (zhp == NULL) {
|
||||
ret = -1;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* open the zpool handle */
|
||||
cp = strchr(namebuf, '/');
|
||||
if (cp != NULL)
|
||||
*cp = '\0';
|
||||
zpool_hdl = zpool_open(hdl, namebuf);
|
||||
if (zpool_hdl == NULL) {
|
||||
ret = -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* restore namebuf to match fsname for later use */
|
||||
if (cp != NULL)
|
||||
*cp = '/';
|
||||
|
||||
/*
|
||||
* first iteration: process excluded (-x) properties now and gather
|
||||
* added (-o) properties to be later processed by zfs_valid_proplist()
|
||||
|
@ -3509,6 +3539,17 @@ zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type, boolean_t zoned,
|
|||
goto error;
|
||||
}
|
||||
|
||||
/* raw streams can't override encryption properties */
|
||||
if ((zfs_prop_encryption_key_param(prop) ||
|
||||
prop == ZFS_PROP_ENCRYPTION) && (raw || !newfs)) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"encryption property '%s' cannot "
|
||||
"be set or excluded for raw or incremental "
|
||||
"streams."), name);
|
||||
ret = zfs_error(hdl, EZFS_BADPROP, errbuf);
|
||||
goto error;
|
||||
}
|
||||
|
||||
switch (nvpair_type(nvp)) {
|
||||
case DATA_TYPE_BOOLEAN: /* -x property */
|
||||
/*
|
||||
|
@ -3559,6 +3600,21 @@ zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type, boolean_t zoned,
|
|||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* zfs_crypto_create() requires the parent name. Get it
|
||||
* by truncating the fsname copy stored in namebuf.
|
||||
*/
|
||||
cp = strrchr(namebuf, '/');
|
||||
if (cp != NULL)
|
||||
*cp = '\0';
|
||||
|
||||
if (!raw && zfs_crypto_create(hdl, namebuf, voprops, NULL,
|
||||
B_FALSE, wkeydata_out, wkeylen_out) != 0) {
|
||||
fnvlist_free(voprops);
|
||||
ret = zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* second pass: process "-o" properties */
|
||||
fnvlist_merge(*oxprops, voprops);
|
||||
fnvlist_free(voprops);
|
||||
|
@ -3572,6 +3628,10 @@ zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type, boolean_t zoned,
|
|||
}
|
||||
|
||||
error:
|
||||
if (zhp != NULL)
|
||||
zfs_close(zhp);
|
||||
if (zpool_hdl != NULL)
|
||||
zpool_close(zpool_hdl);
|
||||
fnvlist_free(oprops);
|
||||
return (ret);
|
||||
}
|
||||
|
@ -3615,6 +3675,8 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
|||
boolean_t toplevel = B_FALSE;
|
||||
boolean_t zoned = B_FALSE;
|
||||
boolean_t hastoken = B_FALSE;
|
||||
uint8_t *wkeydata = NULL;
|
||||
uint_t wkeylen = 0;
|
||||
|
||||
begin_time = time(NULL);
|
||||
bzero(origin, MAXNAMELEN);
|
||||
|
@ -4001,9 +4063,13 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
|||
* locally set on the send side would not be received correctly.
|
||||
* We can infer encryption=off if the stream is not raw and
|
||||
* properties were included since the send side will only ever
|
||||
* send the encryption property in a raw nvlist header.
|
||||
* send the encryption property in a raw nvlist header. This
|
||||
* check will be avoided if the user specifically overrides
|
||||
* the encryption property on the command line.
|
||||
*/
|
||||
if (!raw && rcvprops != NULL) {
|
||||
if (!raw && rcvprops != NULL &&
|
||||
!nvlist_exists(cmdprops,
|
||||
zfs_prop_to_name(ZFS_PROP_ENCRYPTION))) {
|
||||
uint64_t crypt;
|
||||
|
||||
zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
|
||||
|
@ -4053,13 +4119,15 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
|||
err = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
|
||||
goto out;
|
||||
}
|
||||
if ((err = zfs_setup_cmdline_props(hdl, type, zoned, recursive,
|
||||
toplevel, rcvprops, cmdprops, origprops, &oxprops, errbuf)) != 0)
|
||||
if ((err = zfs_setup_cmdline_props(hdl, type, name, zoned, recursive,
|
||||
stream_wantsnewfs, raw, toplevel, rcvprops, cmdprops, origprops,
|
||||
&oxprops, &wkeydata, &wkeylen, errbuf)) != 0)
|
||||
goto out;
|
||||
|
||||
err = ioctl_err = lzc_receive_with_cmdprops(destsnap, rcvprops, oxprops,
|
||||
origin, flags->force, flags->resumable, raw, infd, drr_noswap,
|
||||
cleanup_fd, &read_bytes, &errflags, action_handlep, &prop_errors);
|
||||
err = ioctl_err = lzc_receive_with_cmdprops(destsnap, rcvprops,
|
||||
oxprops, wkeydata, wkeylen, origin, flags->force, flags->resumable,
|
||||
raw, infd, drr_noswap, cleanup_fd, &read_bytes, &errflags,
|
||||
action_handlep, &prop_errors);
|
||||
ioctl_errno = ioctl_err;
|
||||
prop_errflags = errflags;
|
||||
|
||||
|
@ -4346,6 +4414,11 @@ zfs_receive_checkprops(libzfs_handle_t *hdl, nvlist_t *props,
|
|||
if (prop == ZFS_PROP_ORIGIN)
|
||||
continue;
|
||||
|
||||
/* encryption params have their own verification later */
|
||||
if (prop == ZFS_PROP_ENCRYPTION ||
|
||||
zfs_prop_encryption_key_param(prop))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* cannot override readonly, set-once and other specific
|
||||
* settable properties
|
||||
|
|
|
@ -662,8 +662,9 @@ recv_read(int fd, void *buf, int ilen)
|
|||
*/
|
||||
static int
|
||||
recv_impl(const char *snapname, nvlist_t *recvdprops, nvlist_t *localprops,
|
||||
const char *origin, boolean_t force, boolean_t resumable, boolean_t raw,
|
||||
int input_fd, const dmu_replay_record_t *begin_record, int cleanup_fd,
|
||||
uint8_t *wkeydata, uint_t wkeylen, const char *origin, boolean_t force,
|
||||
boolean_t resumable, boolean_t raw, int input_fd,
|
||||
const dmu_replay_record_t *begin_record, int cleanup_fd,
|
||||
uint64_t *read_bytes, uint64_t *errflags, uint64_t *action_handle,
|
||||
nvlist_t **errors)
|
||||
{
|
||||
|
@ -703,7 +704,11 @@ recv_impl(const char *snapname, nvlist_t *recvdprops, nvlist_t *localprops,
|
|||
drr = *begin_record;
|
||||
}
|
||||
|
||||
if (resumable || raw) {
|
||||
/*
|
||||
* Raw receives, resumable receives, and receives that include a
|
||||
* wrapping key all use the new interface.
|
||||
*/
|
||||
if (resumable || raw || wkeydata != NULL) {
|
||||
nvlist_t *outnvl = NULL;
|
||||
nvlist_t *innvl = fnvlist_alloc();
|
||||
|
||||
|
@ -715,6 +720,20 @@ recv_impl(const char *snapname, nvlist_t *recvdprops, nvlist_t *localprops,
|
|||
if (localprops != NULL)
|
||||
fnvlist_add_nvlist(innvl, "localprops", localprops);
|
||||
|
||||
if (wkeydata != NULL) {
|
||||
/*
|
||||
* wkeydata must be placed in the special
|
||||
* ZPOOL_HIDDEN_ARGS nvlist so that it
|
||||
* will not be printed to the zpool history.
|
||||
*/
|
||||
nvlist_t *hidden_args = fnvlist_alloc();
|
||||
fnvlist_add_uint8_array(hidden_args, "wkeydata",
|
||||
wkeydata, wkeylen);
|
||||
fnvlist_add_nvlist(innvl, ZPOOL_HIDDEN_ARGS,
|
||||
hidden_args);
|
||||
nvlist_free(hidden_args);
|
||||
}
|
||||
|
||||
if (origin != NULL && strlen(origin))
|
||||
fnvlist_add_string(innvl, "origin", origin);
|
||||
|
||||
|
@ -846,8 +865,8 @@ int
|
|||
lzc_receive(const char *snapname, nvlist_t *props, const char *origin,
|
||||
boolean_t force, boolean_t raw, int fd)
|
||||
{
|
||||
return (recv_impl(snapname, props, NULL, origin, force, B_FALSE, raw,
|
||||
fd, NULL, -1, NULL, NULL, NULL, NULL));
|
||||
return (recv_impl(snapname, props, NULL, NULL, 0, origin, force,
|
||||
B_FALSE, raw, fd, NULL, -1, NULL, NULL, NULL, NULL));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -860,8 +879,8 @@ int
|
|||
lzc_receive_resumable(const char *snapname, nvlist_t *props, const char *origin,
|
||||
boolean_t force, boolean_t raw, int fd)
|
||||
{
|
||||
return (recv_impl(snapname, props, NULL, origin, force, B_TRUE, raw,
|
||||
fd, NULL, -1, NULL, NULL, NULL, NULL));
|
||||
return (recv_impl(snapname, props, NULL, NULL, 0, origin, force,
|
||||
B_TRUE, raw, fd, NULL, -1, NULL, NULL, NULL, NULL));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -883,8 +902,8 @@ lzc_receive_with_header(const char *snapname, nvlist_t *props,
|
|||
if (begin_record == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
return (recv_impl(snapname, props, NULL, origin, force, resumable, raw,
|
||||
fd, begin_record, -1, NULL, NULL, NULL, NULL));
|
||||
return (recv_impl(snapname, props, NULL, NULL, 0, origin, force,
|
||||
resumable, raw, fd, begin_record, -1, NULL, NULL, NULL, NULL));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -913,9 +932,9 @@ int lzc_receive_one(const char *snapname, nvlist_t *props,
|
|||
uint64_t *read_bytes, uint64_t *errflags, uint64_t *action_handle,
|
||||
nvlist_t **errors)
|
||||
{
|
||||
return (recv_impl(snapname, props, NULL, origin, force, resumable,
|
||||
raw, input_fd, begin_record, cleanup_fd, read_bytes, errflags,
|
||||
action_handle, errors));
|
||||
return (recv_impl(snapname, props, NULL, NULL, 0, origin, force,
|
||||
resumable, raw, input_fd, begin_record, cleanup_fd, read_bytes,
|
||||
errflags, action_handle, errors));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -927,15 +946,15 @@ int lzc_receive_one(const char *snapname, nvlist_t *props,
|
|||
* this nvlist
|
||||
*/
|
||||
int lzc_receive_with_cmdprops(const char *snapname, nvlist_t *props,
|
||||
nvlist_t *cmdprops, const char *origin, boolean_t force,
|
||||
boolean_t resumable, boolean_t raw, int input_fd,
|
||||
nvlist_t *cmdprops, uint8_t *wkeydata, uint_t wkeylen, const char *origin,
|
||||
boolean_t force, boolean_t resumable, boolean_t raw, int input_fd,
|
||||
const dmu_replay_record_t *begin_record, int cleanup_fd,
|
||||
uint64_t *read_bytes, uint64_t *errflags, uint64_t *action_handle,
|
||||
nvlist_t **errors)
|
||||
{
|
||||
return (recv_impl(snapname, props, cmdprops, origin, force, resumable,
|
||||
raw, input_fd, begin_record, cleanup_fd, read_bytes, errflags,
|
||||
action_handle, errors));
|
||||
return (recv_impl(snapname, props, cmdprops, wkeydata, wkeylen, origin,
|
||||
force, resumable, raw, input_fd, begin_record, cleanup_fd,
|
||||
read_bytes, errflags, action_handle, errors));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -3912,6 +3912,34 @@ results if the same property is specified in multiple
|
|||
or
|
||||
.Fl x
|
||||
options.
|
||||
.Pp
|
||||
The
|
||||
.Fl o
|
||||
option may also be used to override encryption properties upon initial
|
||||
receive. This allows unencrypted streams to be received as encrypted datasets.
|
||||
To cause the received dataset (or root dataset of a recursive stream) to be
|
||||
received as an encryption root, specify encryption properties in the same
|
||||
manner as is required for
|
||||
.Nm
|
||||
.Cm create .
|
||||
For instance:
|
||||
.Bd -literal
|
||||
# zfs send tank/test@snap1 | zfs recv -o encryption=on -o keyformat=passphrase -o keylocation=file:///path/to/keyfile
|
||||
.Ed
|
||||
.Pp
|
||||
Note that
|
||||
.Op Fl o Ar keylocation Ns = Ns Ar prompt
|
||||
may not be specified here, since stdin is already being utilized for the send
|
||||
stream. Once the receive has completed, you can use
|
||||
.Nm
|
||||
.Cm set
|
||||
to change this setting after the fact. Similarly, you can receive a dataset as
|
||||
an encrypted child by specifying
|
||||
.Op Fl x Ar encryption
|
||||
to force the property to be inherited. Overriding encryption properties (except
|
||||
for
|
||||
.Sy keylocation Ns )
|
||||
is not possible with raw send streams.
|
||||
.It Fl s
|
||||
If the receive is interrupted, save the partially received state, rather
|
||||
than deleting it.
|
||||
|
|
|
@ -1526,18 +1526,17 @@ typedef struct dmu_recv_begin_arg {
|
|||
const char *drba_origin;
|
||||
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
|
||||
recv_begin_check_existing_impl(dmu_recv_begin_arg_t *drba, dsl_dataset_t *ds,
|
||||
uint64_t fromguid)
|
||||
uint64_t fromguid, uint64_t featureflags)
|
||||
{
|
||||
uint64_t val;
|
||||
int error;
|
||||
dsl_pool_t *dp = ds->ds_dir->dd_pool;
|
||||
struct drr_begin *drrb = drba->drba_cookie->drc_drrb;
|
||||
uint64_t featureflags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo);
|
||||
boolean_t encrypted = ds->ds_dir->dd_crypto_obj != 0;
|
||||
boolean_t raw = (featureflags & DMU_BACKUP_FEATURE_RAW) != 0;
|
||||
|
||||
|
@ -1624,6 +1623,13 @@ recv_begin_check_existing_impl(dmu_recv_begin_arg_t *drba, dsl_dataset_t *ds,
|
|||
if ((!encrypted && raw) || encrypted)
|
||||
return (SET_ERROR(EINVAL));
|
||||
|
||||
if ((featureflags & DMU_BACKUP_FEATURE_RAW) == 0) {
|
||||
error = dmu_objset_create_crypt_check(
|
||||
ds->ds_dir->dd_parent, drba->drba_dcp);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
}
|
||||
|
||||
drba->drba_snapobj = 0;
|
||||
}
|
||||
|
||||
|
@ -1690,7 +1696,7 @@ dmu_recv_begin_check(void *arg, dmu_tx_t *tx)
|
|||
!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_LARGE_DNODE))
|
||||
return (SET_ERROR(ENOTSUP));
|
||||
|
||||
if ((featureflags & DMU_BACKUP_FEATURE_RAW)) {
|
||||
if (featureflags & DMU_BACKUP_FEATURE_RAW) {
|
||||
/* raw receives require the encryption feature */
|
||||
if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_ENCRYPTION))
|
||||
return (SET_ERROR(ENOTSUP));
|
||||
|
@ -1708,7 +1714,8 @@ dmu_recv_begin_check(void *arg, dmu_tx_t *tx)
|
|||
return (SET_ERROR(EINVAL));
|
||||
}
|
||||
|
||||
error = recv_begin_check_existing_impl(drba, ds, fromguid);
|
||||
error = recv_begin_check_existing_impl(drba, ds, fromguid,
|
||||
featureflags);
|
||||
dsl_dataset_rele_flags(ds, dsflags, FTAG);
|
||||
} else if (error == ENOENT) {
|
||||
/* target fs does not exist; must be a full backup or clone */
|
||||
|
@ -1738,6 +1745,16 @@ dmu_recv_begin_check(void *arg, dmu_tx_t *tx)
|
|||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
if ((featureflags & DMU_BACKUP_FEATURE_RAW) == 0 &&
|
||||
drba->drba_origin == NULL) {
|
||||
error = dmu_objset_create_crypt_check(ds->ds_dir,
|
||||
drba->drba_dcp);
|
||||
if (error != 0) {
|
||||
dsl_dataset_rele_flags(ds, dsflags, FTAG);
|
||||
return (error);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check filesystem and snapshot limits before receiving. We'll
|
||||
* recheck snapshot limits again at the end (we create the
|
||||
|
@ -1801,15 +1818,27 @@ dmu_recv_begin_sync(void *arg, dmu_tx_t *tx)
|
|||
ds_hold_flags_t dsflags = 0;
|
||||
int error;
|
||||
uint64_t crflags = 0;
|
||||
dsl_crypto_params_t *dcpp = NULL;
|
||||
dsl_crypto_params_t dcp = { 0 };
|
||||
dsl_crypto_params_t dummy_dcp = { 0 };
|
||||
dsl_crypto_params_t *dcp = drba->drba_dcp;
|
||||
|
||||
if (drrb->drr_flags & DRR_FLAG_CI_DATA)
|
||||
crflags |= DS_FLAG_CI_DATASET;
|
||||
if ((featureflags & DMU_BACKUP_FEATURE_RAW) == 0) {
|
||||
|
||||
if ((featureflags & DMU_BACKUP_FEATURE_RAW) == 0)
|
||||
dsflags |= DS_HOLD_FLAG_DECRYPT;
|
||||
} else {
|
||||
dcp.cp_cmd = DCP_CMD_RAW_RECV;
|
||||
|
||||
/*
|
||||
* Raw, non-incremental recvs always use a dummy dcp with
|
||||
* 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 &&
|
||||
drba->drba_origin == NULL) {
|
||||
ASSERT3P(dcp, ==, NULL);
|
||||
dcp = &dummy_dcp;
|
||||
|
||||
if (featureflags & DMU_BACKUP_FEATURE_RAW)
|
||||
dcp->cp_cmd = DCP_CMD_RAW_RECV;
|
||||
}
|
||||
|
||||
error = dsl_dataset_hold_flags(dp, tofs, dsflags, FTAG, &ds);
|
||||
|
@ -1820,13 +1849,11 @@ dmu_recv_begin_sync(void *arg, dmu_tx_t *tx)
|
|||
if (drba->drba_snapobj != 0) {
|
||||
VERIFY0(dsl_dataset_hold_obj(dp,
|
||||
drba->drba_snapobj, FTAG, &snap));
|
||||
} else {
|
||||
/* we use the dcp whenever we are not making a clone */
|
||||
dcpp = &dcp;
|
||||
ASSERT3P(dcp, ==, NULL);
|
||||
}
|
||||
|
||||
dsobj = dsl_dataset_create_sync(ds->ds_dir, recv_clone_name,
|
||||
snap, crflags, drba->drba_cred, dcpp, tx);
|
||||
snap, crflags, drba->drba_cred, dcp, tx);
|
||||
if (drba->drba_snapobj != 0)
|
||||
dsl_dataset_rele(snap, FTAG);
|
||||
dsl_dataset_rele_flags(ds, dsflags, FTAG);
|
||||
|
@ -1840,19 +1867,18 @@ dmu_recv_begin_sync(void *arg, dmu_tx_t *tx)
|
|||
if (drba->drba_origin != NULL) {
|
||||
VERIFY0(dsl_dataset_hold(dp, drba->drba_origin,
|
||||
FTAG, &origin));
|
||||
} else {
|
||||
/* we use the dcp whenever we are not making a clone */
|
||||
dcpp = &dcp;
|
||||
ASSERT3P(dcp, ==, NULL);
|
||||
}
|
||||
|
||||
/* Create new dataset. */
|
||||
dsobj = dsl_dataset_create_sync(dd, strrchr(tofs, '/') + 1,
|
||||
origin, crflags, drba->drba_cred, dcpp, tx);
|
||||
origin, crflags, drba->drba_cred, dcp, tx);
|
||||
if (origin != NULL)
|
||||
dsl_dataset_rele(origin, FTAG);
|
||||
dsl_dir_rele(dd, FTAG);
|
||||
drba->drba_cookie->drc_newfs = B_TRUE;
|
||||
}
|
||||
|
||||
VERIFY0(dsl_dataset_own_obj(dp, dsobj, dsflags, dmu_recv_tag, &newds));
|
||||
VERIFY0(dmu_objset_from_ds(newds, &os));
|
||||
|
||||
|
@ -2103,7 +2129,8 @@ dmu_recv_resume_begin_sync(void *arg, dmu_tx_t *tx)
|
|||
*/
|
||||
int
|
||||
dmu_recv_begin(char *tofs, char *tosnap, dmu_replay_record_t *drr_begin,
|
||||
boolean_t force, boolean_t resumable, char *origin, dmu_recv_cookie_t *drc)
|
||||
boolean_t force, boolean_t resumable, nvlist_t *localprops,
|
||||
nvlist_t *hidden_args, char *origin, dmu_recv_cookie_t *drc)
|
||||
{
|
||||
dmu_recv_begin_arg_t drba = { 0 };
|
||||
|
||||
|
@ -2139,9 +2166,33 @@ dmu_recv_begin(char *tofs, char *tosnap, dmu_replay_record_t *drr_begin,
|
|||
dmu_recv_resume_begin_check, dmu_recv_resume_begin_sync,
|
||||
&drba, 5, ZFS_SPACE_CHECK_NORMAL));
|
||||
} else {
|
||||
return (dsl_sync_task(tofs,
|
||||
int err;
|
||||
|
||||
/*
|
||||
* For non-raw, non-incremental, non-resuming receives the
|
||||
* user can specify encryption parameters on the command line
|
||||
* with "zfs recv -o". For these receives we create a dcp and
|
||||
* pass it to the sync task. Creating the dcp will implicitly
|
||||
* remove the encryption params from the localprops nvlist,
|
||||
* which avoids errors when trying to set these normally
|
||||
* read-only properties. Any other kind of receive that
|
||||
* attempts to set these properties will fail as a result.
|
||||
*/
|
||||
if ((DMU_GET_FEATUREFLAGS(drc->drc_drrb->drr_versioninfo) &
|
||||
DMU_BACKUP_FEATURE_RAW) == 0 &&
|
||||
origin == NULL && drc->drc_drrb->drr_fromguid == 0) {
|
||||
err = dsl_crypto_params_create_nvlist(DCP_CMD_NONE,
|
||||
localprops, hidden_args, &drba.drba_dcp);
|
||||
if (err != 0)
|
||||
return (err);
|
||||
}
|
||||
|
||||
err = dsl_sync_task(tofs,
|
||||
dmu_recv_begin_check, dmu_recv_begin_sync,
|
||||
&drba, 5, ZFS_SPACE_CHECK_NORMAL));
|
||||
&drba, 5, ZFS_SPACE_CHECK_NORMAL);
|
||||
dsl_crypto_params_free(drba.drba_dcp, !!err);
|
||||
|
||||
return (err);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1719,6 +1719,10 @@ dmu_objset_create_crypt_check(dsl_dir_t *parentdd, dsl_crypto_params_t *dcp)
|
|||
{
|
||||
int ret;
|
||||
uint64_t pcrypt, crypt;
|
||||
dsl_crypto_params_t dummy_dcp = { 0 };
|
||||
|
||||
if (dcp == NULL)
|
||||
dcp = &dummy_dcp;
|
||||
|
||||
if (dcp->cp_cmd != DCP_CMD_NONE)
|
||||
return (SET_ERROR(EINVAL));
|
||||
|
|
|
@ -4339,15 +4339,17 @@ static boolean_t zfs_ioc_recv_inject_err;
|
|||
*/
|
||||
static int
|
||||
zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin, nvlist_t *recvprops,
|
||||
nvlist_t *localprops, boolean_t force, boolean_t resumable, int input_fd,
|
||||
dmu_replay_record_t *begin_record, int cleanup_fd, uint64_t *read_bytes,
|
||||
uint64_t *errflags, uint64_t *action_handle, nvlist_t **errors)
|
||||
nvlist_t *localprops, nvlist_t *hidden_args, boolean_t force,
|
||||
boolean_t resumable, int input_fd, dmu_replay_record_t *begin_record,
|
||||
int cleanup_fd, uint64_t *read_bytes, uint64_t *errflags,
|
||||
uint64_t *action_handle, nvlist_t **errors)
|
||||
{
|
||||
dmu_recv_cookie_t drc;
|
||||
int error = 0;
|
||||
int props_error = 0;
|
||||
offset_t off;
|
||||
nvlist_t *delayprops = NULL; /* sent properties applied post-receive */
|
||||
nvlist_t *local_delayprops = NULL;
|
||||
nvlist_t *recv_delayprops = NULL;
|
||||
nvlist_t *origprops = NULL; /* existing properties */
|
||||
nvlist_t *origrecvd = NULL; /* existing received properties */
|
||||
boolean_t first_recvd_props = B_FALSE;
|
||||
|
@ -4361,8 +4363,8 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin, nvlist_t *recvprops,
|
|||
if (input_fp == NULL)
|
||||
return (SET_ERROR(EBADF));
|
||||
|
||||
error = dmu_recv_begin(tofs, tosnap,
|
||||
begin_record, force, resumable, origin, &drc);
|
||||
error = dmu_recv_begin(tofs, tosnap, begin_record, force,
|
||||
resumable, localprops, hidden_args, origin, &drc);
|
||||
if (error != 0)
|
||||
goto out;
|
||||
|
||||
|
@ -4379,8 +4381,8 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin, nvlist_t *recvprops,
|
|||
|
||||
/*
|
||||
* If new received properties are supplied, they are to
|
||||
* completely replace the existing received properties, so stash
|
||||
* away the existing ones.
|
||||
* completely replace the existing received properties,
|
||||
* so stash away the existing ones.
|
||||
*/
|
||||
if (dsl_prop_get_received(tofs, &origrecvd) == 0) {
|
||||
nvlist_t *errlist = NULL;
|
||||
|
@ -4427,7 +4429,7 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin, nvlist_t *recvprops,
|
|||
props_error = dsl_prop_set_hasrecvd(tofs);
|
||||
|
||||
if (props_error == 0) {
|
||||
delayprops = extract_delay_props(recvprops);
|
||||
recv_delayprops = extract_delay_props(recvprops);
|
||||
(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
|
||||
recvprops, *errors);
|
||||
}
|
||||
|
@ -4454,6 +4456,8 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin, nvlist_t *recvprops,
|
|||
fnvlist_add_nvpair(oprops, nvp);
|
||||
}
|
||||
}
|
||||
|
||||
local_delayprops = extract_delay_props(oprops);
|
||||
(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_LOCAL,
|
||||
oprops, *errors);
|
||||
(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_INHERITED,
|
||||
|
@ -4495,26 +4499,33 @@ zfs_ioc_recv_impl(char *tofs, char *tosnap, char *origin, nvlist_t *recvprops,
|
|||
}
|
||||
|
||||
/* Set delayed properties now, after we're done receiving. */
|
||||
if (delayprops != NULL && error == 0) {
|
||||
if (recv_delayprops != NULL && error == 0) {
|
||||
(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
|
||||
delayprops, *errors);
|
||||
recv_delayprops, *errors);
|
||||
}
|
||||
if (local_delayprops != NULL && error == 0) {
|
||||
(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_LOCAL,
|
||||
local_delayprops, *errors);
|
||||
}
|
||||
}
|
||||
|
||||
if (delayprops != NULL) {
|
||||
/*
|
||||
* Merge delayed props back in with initial props, in case
|
||||
* we're DEBUG and zfs_ioc_recv_inject_err is set (which means
|
||||
* we have to make sure clear_received_props() includes
|
||||
* the delayed properties).
|
||||
*
|
||||
* Since zfs_ioc_recv_inject_err is only in DEBUG kernels,
|
||||
* using ASSERT() will be just like a VERIFY.
|
||||
*/
|
||||
ASSERT(nvlist_merge(recvprops, delayprops, 0) == 0);
|
||||
nvlist_free(delayprops);
|
||||
/*
|
||||
* Merge delayed props back in with initial props, in case
|
||||
* we're DEBUG and zfs_ioc_recv_inject_err is set (which means
|
||||
* we have to make sure clear_received_props() includes
|
||||
* the delayed properties).
|
||||
*
|
||||
* Since zfs_ioc_recv_inject_err is only in DEBUG kernels,
|
||||
* using ASSERT() will be just like a VERIFY.
|
||||
*/
|
||||
if (recv_delayprops != NULL) {
|
||||
ASSERT(nvlist_merge(recvprops, recv_delayprops, 0) == 0);
|
||||
nvlist_free(recv_delayprops);
|
||||
}
|
||||
if (local_delayprops != NULL) {
|
||||
ASSERT(nvlist_merge(localprops, local_delayprops, 0) == 0);
|
||||
nvlist_free(local_delayprops);
|
||||
}
|
||||
|
||||
|
||||
*read_bytes = off - input_fp->f_offset;
|
||||
if (VOP_SEEK(input_fp->f_vnode, input_fp->f_offset, &off, NULL) == 0)
|
||||
|
@ -4689,7 +4700,7 @@ zfs_ioc_recv(zfs_cmd_t *zc)
|
|||
begin_record.drr_u.drr_begin = zc->zc_begin_record;
|
||||
|
||||
error = zfs_ioc_recv_impl(tofs, tosnap, origin, recvdprops, localprops,
|
||||
zc->zc_guid, B_FALSE, zc->zc_cookie, &begin_record,
|
||||
NULL, zc->zc_guid, B_FALSE, zc->zc_cookie, &begin_record,
|
||||
zc->zc_cleanup_fd, &zc->zc_cookie, &zc->zc_obj,
|
||||
&zc->zc_action_handle, &errors);
|
||||
nvlist_free(recvdprops);
|
||||
|
@ -4743,6 +4754,7 @@ zfs_ioc_recv_new(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
|
|||
nvlist_t *errors = NULL;
|
||||
nvlist_t *recvprops = NULL;
|
||||
nvlist_t *localprops = NULL;
|
||||
nvlist_t *hidden_args = NULL;
|
||||
char *snapname = NULL;
|
||||
char *origin = NULL;
|
||||
char *tosnap;
|
||||
|
@ -4802,9 +4814,13 @@ zfs_ioc_recv_new(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
|
|||
if (error && error != ENOENT)
|
||||
return (error);
|
||||
|
||||
error = nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, &hidden_args);
|
||||
if (error && error != ENOENT)
|
||||
return (error);
|
||||
|
||||
error = zfs_ioc_recv_impl(tofs, tosnap, origin, recvprops, localprops,
|
||||
force, resumable, input_fd, begin_record, cleanup_fd, &read_bytes,
|
||||
&errflags, &action_handle, &errors);
|
||||
hidden_args, force, resumable, input_fd, begin_record, cleanup_fd,
|
||||
&read_bytes, &errflags, &action_handle, &errors);
|
||||
|
||||
fnvlist_add_uint64(outnvl, "read_bytes", read_bytes);
|
||||
fnvlist_add_uint64(outnvl, "error_flags", errflags);
|
||||
|
|
|
@ -455,7 +455,7 @@ tests = ['zdb_001_neg', 'zfs_001_neg', 'zfs_allow_001_neg',
|
|||
'zpool_offline_001_neg', 'zpool_online_001_neg', 'zpool_remove_001_neg',
|
||||
'zpool_replace_001_neg', 'zpool_scrub_001_neg', 'zpool_set_001_neg',
|
||||
'zpool_status_001_neg', 'zpool_upgrade_001_neg', 'arcstat_001_pos',
|
||||
'arc_summary_001_pos', 'arc_summary_002_neg',
|
||||
'arc_summary_001_pos', 'arc_summary_002_neg',
|
||||
'arc_summary3_001_pos', 'dbufstat_001_pos']
|
||||
user =
|
||||
tags = ['functional', 'cli_user', 'misc']
|
||||
|
@ -743,7 +743,8 @@ tests = ['rsend_001_pos', 'rsend_002_pos', 'rsend_003_pos', 'rsend_004_pos',
|
|||
'send-c_mixed_compression', 'send-c_stream_size_estimate', 'send-cD',
|
||||
'send-c_embedded_blocks', 'send-c_resume', 'send-cpL_varied_recsize',
|
||||
'send-c_recv_dedup', 'send_encrypted_files', 'send_encrypted_heirarchy',
|
||||
'send_freeobjects', 'send_realloc_dnode_size', 'send_hole_birth']
|
||||
'send_encrypted_props', 'send_freeobjects', 'send_realloc_dnode_size',
|
||||
'send_hole_birth']
|
||||
tags = ['functional', 'rsend']
|
||||
|
||||
[tests/functional/scrub_mirror]
|
||||
|
|
|
@ -23,6 +23,7 @@ dist_pkgdata_SCRIPTS = \
|
|||
rsend_024_pos.ksh \
|
||||
send_encrypted_files.ksh \
|
||||
send_encrypted_heirarchy.ksh \
|
||||
send_encrypted_props.ksh \
|
||||
send-cD.ksh \
|
||||
send-c_embedded_blocks.ksh \
|
||||
send-c_incremental.ksh \
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
#!/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) 2018 by Datto Inc. All rights reserved.
|
||||
#
|
||||
|
||||
. $STF_SUITE/tests/functional/rsend/rsend.kshlib
|
||||
|
||||
#
|
||||
# DESCRIPTION:
|
||||
# Verify that zfs properly handles encryption properties when receiving
|
||||
# send streams.
|
||||
#
|
||||
# STRATEGY:
|
||||
# 1. Create a few unencrypted and encrypted test datasets with some data
|
||||
# 2. Take snapshots of these datasets in preparation for sending
|
||||
# 3. Verify that 'zfs recv -o keylocation=prompt' fails
|
||||
# 4. Verify that 'zfs recv -x <encryption prop>' fails on a raw send stream
|
||||
# 5. Verify that encryption properties cannot be changed on incrementals
|
||||
# 6. Verify that a simple send can be received as an encryption root
|
||||
# 7. Verify that an unencrypted props send can be received as an
|
||||
# encryption root
|
||||
# 8. Verify that an unencrypted recursive send can be received as an
|
||||
# encryption root
|
||||
# 9. Verify that an unencrypted props send can be received as an
|
||||
# encryption child
|
||||
# 10. Verify that an unencrypted recursive send can be received as an
|
||||
# encryption child
|
||||
#
|
||||
|
||||
verify_runnable "both"
|
||||
|
||||
function cleanup
|
||||
{
|
||||
destroy_dataset $TESTPOOL/recv "-r"
|
||||
destroy_dataset $TESTPOOL/crypt "-r"
|
||||
destroy_dataset $TESTPOOL/ds "-r"
|
||||
[[ -f $sendfile ]] && log_must rm $sendfile
|
||||
[[ -f $keyfile ]] && log_must rm $keyfile
|
||||
}
|
||||
log_onexit cleanup
|
||||
|
||||
log_assert "'zfs recv' must properly handle encryption properties"
|
||||
|
||||
typeset keyfile=/$TESTPOOL/pkey
|
||||
typeset sendfile=/$TESTPOOL/sendfile
|
||||
typeset snap=$TESTPOOL/ds@snap
|
||||
typeset esnap=$TESTPOOL/crypt@snap1
|
||||
typeset esnap2=$TESTPOOL/crypt@snap2
|
||||
|
||||
log_must eval "echo 'password' > $keyfile"
|
||||
|
||||
log_must zfs create $TESTPOOL/ds
|
||||
log_must zfs create $TESTPOOL/ds/ds1
|
||||
|
||||
log_must zfs create -o encryption=on -o keyformat=passphrase \
|
||||
-o keylocation=file://$keyfile $TESTPOOL/crypt
|
||||
log_must zfs create $TESTPOOL/crypt/ds1
|
||||
log_must zfs create -o keyformat=passphrase -o keylocation=file://$keyfile \
|
||||
$TESTPOOL/crypt/ds2
|
||||
|
||||
log_must mkfile 1M /$TESTPOOL/ds/$TESTFILE0
|
||||
log_must cp /$TESTPOOL/ds/$TESTFILE0 /$TESTPOOL/crypt/$TESTFILE0
|
||||
typeset cksum=$(md5sum /$TESTPOOL/ds/$TESTFILE0 | awk '{ print $1 }')
|
||||
|
||||
log_must zfs snap -r $snap
|
||||
log_must zfs snap -r $esnap
|
||||
log_must zfs snap -r $esnap2
|
||||
|
||||
# Embedded data is incompatible with encrypted datasets, so we cannot
|
||||
# allow embedded streams to be received.
|
||||
log_note "Must not be able to receive an embedded stream as encrypted"
|
||||
log_mustnot eval "zfs send -e $TESTPOOL/crypt/ds1 | zfs recv $TESTPOOL/recv"
|
||||
|
||||
# We currently don't have an elegant and secure way to pass the passphrase
|
||||
# in via prompt because the send stream itself is using stdin.
|
||||
log_note "Must not be able to use 'keylocation=prompt' on receive"
|
||||
log_must eval "zfs send $snap > $sendfile"
|
||||
log_mustnot eval "zfs recv -o encryption=on -o keyformat=passphrase" \
|
||||
"$TESTPOOL/recv < $sendfile"
|
||||
log_mustnot eval "zfs recv -o encryption=on -o keyformat=passphrase" \
|
||||
"-o keylocation=prompt $TESTPOOL/recv < $sendfile"
|
||||
|
||||
# Raw sends do not have access to the decrypted data so we cannot override
|
||||
# the encryption settings without losing the data.
|
||||
log_note "Must not be able to disable encryption properties on raw send"
|
||||
log_must eval "zfs send -w $esnap > $sendfile"
|
||||
log_mustnot eval "zfs recv -x encryption $TESTPOOL/recv < $sendfile"
|
||||
log_mustnot eval "zfs recv -x keyformat $TESTPOOL/recv < $sendfile"
|
||||
log_mustnot eval "zfs recv -x pbkdf2iters $TESTPOOL/recv < $sendfile"
|
||||
|
||||
# Encryption properties are set upon creating the dataset. Changing them
|
||||
# afterwards requires using 'zfs change-key' or an update from a raw send.
|
||||
log_note "Must not be able to change encryption properties on incrementals"
|
||||
log_must eval "zfs send $esnap | zfs recv -o encryption=on" \
|
||||
"-o keyformat=passphrase -o keylocation=file://$keyfile $TESTPOOL/recv"
|
||||
log_mustnot eval "zfs send -i $esnap $esnap2 |" \
|
||||
"zfs recv -o encryption=aes-128-ccm $TESTPOOL/recv"
|
||||
log_mustnot eval "zfs send -i $esnap $esnap2 |" \
|
||||
"zfs recv -o keyformat=hex $TESTPOOL/recv"
|
||||
log_mustnot eval "zfs send -i $esnap $esnap2 |" \
|
||||
"zfs recv -o pbkdf2iters=100k $TESTPOOL/recv"
|
||||
log_must zfs destroy -r $TESTPOOL/recv
|
||||
|
||||
# Test that we can receive a simple stream as an encryption root.
|
||||
log_note "Must be able to receive stream as encryption root"
|
||||
ds=$TESTPOOL/recv
|
||||
log_must eval "zfs send $snap > $sendfile"
|
||||
log_must eval "zfs recv -o encryption=on -o keyformat=passphrase" \
|
||||
"-o keylocation=file://$keyfile $ds < $sendfile"
|
||||
log_must test "$(get_prop 'encryption' $ds)" == "aes-256-ccm"
|
||||
log_must test "$(get_prop 'encryptionroot' $ds)" == "$ds"
|
||||
log_must test "$(get_prop 'keyformat' $ds)" == "passphrase"
|
||||
log_must test "$(get_prop 'keylocation' $ds)" == "file://$keyfile"
|
||||
log_must test "$(get_prop 'mounted' $ds)" == "yes"
|
||||
recv_cksum=$(md5sum /$ds/$TESTFILE0 | awk '{ print $1 }')
|
||||
log_must test "$recv_cksum" == "$cksum"
|
||||
log_must zfs destroy -r $ds
|
||||
|
||||
# Test that we can override encryption properties on a properties stream
|
||||
# of an unencrypted dataset, turning it into an encryption root.
|
||||
log_note "Must be able to receive stream with props as encryption root"
|
||||
ds=$TESTPOOL/recv
|
||||
log_must eval "zfs send -p $snap > $sendfile"
|
||||
log_must eval "zfs recv -o encryption=on -o keyformat=passphrase" \
|
||||
"-o keylocation=file://$keyfile $ds < $sendfile"
|
||||
log_must test "$(get_prop 'encryption' $ds)" == "aes-256-ccm"
|
||||
log_must test "$(get_prop 'encryptionroot' $ds)" == "$ds"
|
||||
log_must test "$(get_prop 'keyformat' $ds)" == "passphrase"
|
||||
log_must test "$(get_prop 'keylocation' $ds)" == "file://$keyfile"
|
||||
log_must test "$(get_prop 'mounted' $ds)" == "yes"
|
||||
recv_cksum=$(md5sum /$ds/$TESTFILE0 | awk '{ print $1 }')
|
||||
log_must test "$recv_cksum" == "$cksum"
|
||||
log_must zfs destroy -r $ds
|
||||
|
||||
# Test that we can override encryption properties on a recursive stream
|
||||
# of an unencrypted dataset, turning it into an encryption root. The root
|
||||
# dataset of the stream should become an encryption root with all children
|
||||
# inheriting from it.
|
||||
log_note "Must be able to receive recursive stream as encryption root"
|
||||
ds=$TESTPOOL/recv
|
||||
log_must eval "zfs send -R $snap > $sendfile"
|
||||
log_must eval "zfs recv -o encryption=on -o keyformat=passphrase" \
|
||||
"-o keylocation=file://$keyfile $ds < $sendfile"
|
||||
log_must test "$(get_prop 'encryption' $ds)" == "aes-256-ccm"
|
||||
log_must test "$(get_prop 'encryptionroot' $ds)" == "$ds"
|
||||
log_must test "$(get_prop 'keyformat' $ds)" == "passphrase"
|
||||
log_must test "$(get_prop 'keylocation' $ds)" == "file://$keyfile"
|
||||
log_must test "$(get_prop 'mounted' $ds)" == "yes"
|
||||
recv_cksum=$(md5sum /$ds/$TESTFILE0 | awk '{ print $1 }')
|
||||
log_must test "$recv_cksum" == "$cksum"
|
||||
log_must zfs destroy -r $ds
|
||||
|
||||
# Test that we can override an unencrypted properties stream's encryption
|
||||
# settings, receiving it as an encrypted child.
|
||||
log_note "Must be able to receive stream with props to encrypted child"
|
||||
ds=$TESTPOOL/crypt/recv
|
||||
log_must eval "zfs send -p $snap > $sendfile"
|
||||
log_must eval "zfs recv -x encryption $ds < $sendfile"
|
||||
log_must test "$(get_prop 'encryptionroot' $ds)" == "$TESTPOOL/crypt"
|
||||
log_must test "$(get_prop 'encryption' $ds)" == "aes-256-ccm"
|
||||
log_must test "$(get_prop 'keyformat' $ds)" == "passphrase"
|
||||
log_must test "$(get_prop 'mounted' $ds)" == "yes"
|
||||
recv_cksum=$(md5sum /$ds/$TESTFILE0 | awk '{ print $1 }')
|
||||
log_must test "$recv_cksum" == "$cksum"
|
||||
log_must zfs destroy -r $ds
|
||||
|
||||
# Test that we can override an unencrypted recursive stream's encryption
|
||||
# settings, receiving all datasets as encrypted children.
|
||||
log_note "Must be able to receive recursive stream to encrypted child"
|
||||
ds=$TESTPOOL/crypt/recv
|
||||
log_must eval "zfs send -R $snap > $sendfile"
|
||||
log_must eval "zfs recv -x encryption $ds < $sendfile"
|
||||
log_must test "$(get_prop 'encryptionroot' $ds)" == "$TESTPOOL/crypt"
|
||||
log_must test "$(get_prop 'encryption' $ds)" == "aes-256-ccm"
|
||||
log_must test "$(get_prop 'keyformat' $ds)" == "passphrase"
|
||||
log_must test "$(get_prop 'mounted' $ds)" == "yes"
|
||||
recv_cksum=$(md5sum /$ds/$TESTFILE0 | awk '{ print $1 }')
|
||||
log_must test "$recv_cksum" == "$cksum"
|
||||
log_must zfs destroy -r $ds
|
||||
|
||||
# Check that we haven't printed the key to the zpool history log
|
||||
log_mustnot eval "zpool history -i | grep -q 'wkeydata'"
|
||||
|
||||
log_pass "'zfs recv' properly handles encryption properties"
|
Loading…
Reference in New Issue