mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 02:27:36 +03:00
Native Encryption for ZFS on Linux
This change incorporates three major pieces: The first change is a keystore that manages wrapping and encryption keys for encrypted datasets. These commands mostly involve manipulating the new DSL Crypto Key ZAP Objects that live in the MOS. Each encrypted dataset has its own DSL Crypto Key that is protected with a user's key. This level of indirection allows users to change their keys without re-encrypting their entire datasets. The change implements the new subcommands "zfs load-key", "zfs unload-key" and "zfs change-key" which allow the user to manage their encryption keys and settings. In addition, several new flags and properties have been added to allow dataset creation and to make mounting and unmounting more convenient. The second piece of this patch provides the ability to encrypt, decyrpt, and authenticate protected datasets. Each object set maintains a Merkel tree of Message Authentication Codes that protect the lower layers, similarly to how checksums are maintained. This part impacts the zio layer, which handles the actual encryption and generation of MACs, as well as the ARC and DMU, which need to be able to handle encrypted buffers and protected data. The last addition is the ability to do raw, encrypted sends and receives. The idea here is to send raw encrypted and compressed data and receive it exactly as is on a backup system. This means that the dataset on the receiving system is protected using the same user key that is in use on the sending side. By doing so, datasets can be efficiently backed up to an untrusted system without fear of data being compromised. Reviewed by: Matthew Ahrens <mahrens@delphix.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Jorgen Lundman <lundman@lundman.net> Signed-off-by: Tom Caputi <tcaputi@datto.com> Closes #494 Closes #5769
This commit is contained in:
committed by
Brian Behlendorf
parent
376994828f
commit
b525630342
@@ -28,7 +28,7 @@ if TARGET_ASM_I386
|
||||
ASM_SOURCES_C =
|
||||
ASM_SOURCES_AS =
|
||||
endif
|
||||
|
||||
|
||||
if TARGET_ASM_GENERIC
|
||||
ASM_SOURCES_C =
|
||||
ASM_SOURCES_AS =
|
||||
@@ -81,5 +81,5 @@ nodist_libicp_la_SOURCES = \
|
||||
$(USER_ASM) \
|
||||
$(KERNEL_C) \
|
||||
$(KERNEL_ASM)
|
||||
|
||||
|
||||
libicp_la_LIBADD = -lrt
|
||||
|
||||
@@ -88,4 +88,11 @@
|
||||
*/
|
||||
#define MS_OVERLAY 0x00000004
|
||||
|
||||
/*
|
||||
* MS_CRYPT indicates that encryption keys should be loaded if they are not
|
||||
* already available. This is not defined in glibc, but it is never seen by
|
||||
* the kernel so it will not cause any problems.
|
||||
*/
|
||||
#define MS_CRYPT 0x00000008
|
||||
|
||||
#endif /* _LIBSPL_SYS_MOUNT_H */
|
||||
|
||||
@@ -18,6 +18,7 @@ lib_LTLIBRARIES = libzfs.la
|
||||
USER_C = \
|
||||
libzfs_changelist.c \
|
||||
libzfs_config.c \
|
||||
libzfs_crypto.c \
|
||||
libzfs_dataset.c \
|
||||
libzfs_diff.c \
|
||||
libzfs_fru.c \
|
||||
@@ -30,7 +31,6 @@ USER_C = \
|
||||
libzfs_util.c
|
||||
|
||||
KERNEL_C = \
|
||||
algs/sha2/sha2.c \
|
||||
zfeature_common.c \
|
||||
zfs_comutil.c \
|
||||
zfs_deleg.c \
|
||||
@@ -53,10 +53,12 @@ nodist_libzfs_la_SOURCES = \
|
||||
|
||||
libzfs_la_LIBADD = \
|
||||
$(top_builddir)/lib/libefi/libefi.la \
|
||||
$(top_builddir)/lib/libicp/libicp.la \
|
||||
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
||||
$(top_builddir)/lib/libshare/libshare.la \
|
||||
$(top_builddir)/lib/libtpool/libtpool.la \
|
||||
$(top_builddir)/lib/libuutil/libuutil.la \
|
||||
$(top_builddir)/lib/libzpool/libzpool.la \
|
||||
$(top_builddir)/lib/libzfs_core/libzfs_core.la
|
||||
|
||||
libzfs_la_LIBADD += -lm $(LIBBLKID) $(LIBUDEV)
|
||||
|
||||
@@ -199,6 +199,7 @@ changelist_postfix(prop_changelist_t *clp)
|
||||
boolean_t sharenfs;
|
||||
boolean_t sharesmb;
|
||||
boolean_t mounted;
|
||||
boolean_t needs_key;
|
||||
|
||||
/*
|
||||
* If we are in the global zone, but this dataset is exported
|
||||
@@ -229,9 +230,12 @@ changelist_postfix(prop_changelist_t *clp)
|
||||
shareopts, sizeof (shareopts), NULL, NULL, 0,
|
||||
B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
|
||||
|
||||
needs_key = (zfs_prop_get_int(cn->cn_handle,
|
||||
ZFS_PROP_KEYSTATUS) == ZFS_KEYSTATUS_UNAVAILABLE);
|
||||
|
||||
mounted = zfs_is_mounted(cn->cn_handle, NULL);
|
||||
|
||||
if (!mounted && (cn->cn_mounted ||
|
||||
if (!mounted && !needs_key && (cn->cn_mounted ||
|
||||
((sharenfs || sharesmb || clp->cl_waslegacy) &&
|
||||
(zfs_prop_get_int(cn->cn_handle,
|
||||
ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON)))) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+125
-11
@@ -58,6 +58,7 @@
|
||||
#include <sys/dnode.h>
|
||||
#include <sys/spa.h>
|
||||
#include <sys/zap.h>
|
||||
#include <sys/dsl_crypt.h>
|
||||
#include <libzfs.h>
|
||||
|
||||
#include "zfs_namecheck.h"
|
||||
@@ -965,7 +966,7 @@ zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop)
|
||||
nvlist_t *
|
||||
zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
|
||||
uint64_t zoned, zfs_handle_t *zhp, zpool_handle_t *zpool_hdl,
|
||||
const char *errbuf)
|
||||
boolean_t key_params_ok, const char *errbuf)
|
||||
{
|
||||
nvpair_t *elem;
|
||||
uint64_t intval;
|
||||
@@ -1124,7 +1125,8 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
|
||||
}
|
||||
|
||||
if (zfs_prop_readonly(prop) &&
|
||||
(!zfs_prop_setonce(prop) || zhp != NULL)) {
|
||||
!(zfs_prop_setonce(prop) && zhp == NULL) &&
|
||||
!(zfs_prop_encryption_key_param(prop) && key_params_ok)) {
|
||||
zfs_error_aux(hdl,
|
||||
dgettext(TEXT_DOMAIN, "'%s' is readonly"),
|
||||
propname);
|
||||
@@ -1390,6 +1392,48 @@ badlabel:
|
||||
|
||||
break;
|
||||
|
||||
case ZFS_PROP_KEYLOCATION:
|
||||
if (!zfs_prop_valid_keylocation(strval, B_FALSE)) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"invalid keylocation"));
|
||||
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (zhp != NULL) {
|
||||
uint64_t crypt =
|
||||
zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION);
|
||||
|
||||
if (crypt == ZIO_CRYPT_OFF &&
|
||||
strcmp(strval, "none") != 0) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"keylocation must not be 'none' "
|
||||
"for encrypted datasets"));
|
||||
(void) zfs_error(hdl, EZFS_BADPROP,
|
||||
errbuf);
|
||||
goto error;
|
||||
} else if (crypt != ZIO_CRYPT_OFF &&
|
||||
strcmp(strval, "none") == 0) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"keylocation must be 'none' "
|
||||
"for unencrypted datasets"));
|
||||
(void) zfs_error(hdl, EZFS_BADPROP,
|
||||
errbuf);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZFS_PROP_PBKDF2_ITERS:
|
||||
if (intval < MIN_PBKDF2_ITERATIONS) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"minimum pbkdf2 iterations is %u"),
|
||||
MIN_PBKDF2_ITERATIONS);
|
||||
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
|
||||
case ZFS_PROP_UTF8ONLY:
|
||||
chosen_utf = (int)intval;
|
||||
break;
|
||||
@@ -1453,6 +1497,27 @@ badlabel:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* check encryption properties */
|
||||
if (zhp != NULL) {
|
||||
int64_t crypt = zfs_prop_get_int(zhp,
|
||||
ZFS_PROP_ENCRYPTION);
|
||||
|
||||
switch (prop) {
|
||||
case ZFS_PROP_COPIES:
|
||||
if (crypt != ZIO_CRYPT_OFF && intval > 2) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"encrypted datasets cannot have "
|
||||
"3 copies"));
|
||||
(void) zfs_error(hdl, EZFS_BADPROP,
|
||||
errbuf);
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1609,6 +1674,16 @@ zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err,
|
||||
}
|
||||
break;
|
||||
|
||||
case EACCES:
|
||||
if (prop == ZFS_PROP_KEYLOCATION) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"keylocation may only be set on encryption roots"));
|
||||
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
|
||||
} else {
|
||||
(void) zfs_standard_error(hdl, err, errbuf);
|
||||
}
|
||||
break;
|
||||
|
||||
case EOVERFLOW:
|
||||
/*
|
||||
* This platform can't address a volume this big.
|
||||
@@ -1700,7 +1775,7 @@ zfs_prop_set_list(zfs_handle_t *zhp, nvlist_t *props)
|
||||
|
||||
if ((nvl = zfs_valid_proplist(hdl, zhp->zfs_type, props,
|
||||
zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, zhp->zpool_hdl,
|
||||
errbuf)) == NULL)
|
||||
B_FALSE, errbuf)) == NULL)
|
||||
goto error;
|
||||
|
||||
/*
|
||||
@@ -3155,6 +3230,12 @@ parent_name(const char *path, char *buf, size_t buflen)
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_parent_name(zfs_handle_t *zhp, char *buf, size_t buflen)
|
||||
{
|
||||
return (parent_name(zfs_get_name(zhp), buf, buflen));
|
||||
}
|
||||
|
||||
/*
|
||||
* If accept_ancestor is false, then check to make sure that the given path has
|
||||
* a parent, and that it exists. If accept_ancestor is true, then find the
|
||||
@@ -3373,10 +3454,13 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
|
||||
int ret;
|
||||
uint64_t size = 0;
|
||||
uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
|
||||
char errbuf[1024];
|
||||
uint64_t zoned;
|
||||
enum lzc_dataset_type ost;
|
||||
zpool_handle_t *zpool_handle;
|
||||
uint8_t *wkeydata = NULL;
|
||||
uint_t wkeylen = 0;
|
||||
char errbuf[1024];
|
||||
char parent[ZFS_MAX_DATASET_NAME_LEN];
|
||||
|
||||
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
||||
"cannot create '%s'"), path);
|
||||
@@ -3420,7 +3504,7 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
|
||||
return (-1);
|
||||
|
||||
if (props && (props = zfs_valid_proplist(hdl, type, props,
|
||||
zoned, NULL, zpool_handle, errbuf)) == 0) {
|
||||
zoned, NULL, zpool_handle, B_TRUE, errbuf)) == 0) {
|
||||
zpool_close(zpool_handle);
|
||||
return (-1);
|
||||
}
|
||||
@@ -3472,15 +3556,21 @@ 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) {
|
||||
nvlist_free(props);
|
||||
return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));
|
||||
}
|
||||
|
||||
/* create the dataset */
|
||||
ret = lzc_create(path, ost, props);
|
||||
ret = lzc_create(path, ost, props, wkeydata, wkeylen);
|
||||
nvlist_free(props);
|
||||
if (wkeydata != NULL)
|
||||
free(wkeydata);
|
||||
|
||||
/* check for failure */
|
||||
if (ret != 0) {
|
||||
char parent[ZFS_MAX_DATASET_NAME_LEN];
|
||||
(void) parent_name(path, parent, sizeof (parent));
|
||||
|
||||
switch (errno) {
|
||||
case ENOENT:
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
@@ -3497,6 +3587,13 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
|
||||
"pool must be upgraded to set this "
|
||||
"property or value"));
|
||||
return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
|
||||
|
||||
case EACCES:
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"encryption root's key is not loaded "
|
||||
"or provided"));
|
||||
return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));
|
||||
|
||||
#ifdef _ILP32
|
||||
case EOVERFLOW:
|
||||
/*
|
||||
@@ -3691,10 +3788,15 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
|
||||
type = ZFS_TYPE_FILESYSTEM;
|
||||
}
|
||||
if ((props = zfs_valid_proplist(hdl, type, props, zoned,
|
||||
zhp, zhp->zpool_hdl, errbuf)) == NULL)
|
||||
zhp, zhp->zpool_hdl, B_TRUE, errbuf)) == NULL)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (zfs_crypto_clone_check(hdl, zhp, parent, props) != 0) {
|
||||
nvlist_free(props);
|
||||
return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));
|
||||
}
|
||||
|
||||
ret = lzc_clone(target, zhp->zfs_name, props);
|
||||
nvlist_free(props);
|
||||
|
||||
@@ -3847,7 +3949,7 @@ zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props)
|
||||
|
||||
if (props != NULL &&
|
||||
(props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
|
||||
props, B_FALSE, NULL, zpool_hdl, errbuf)) == NULL) {
|
||||
props, B_FALSE, NULL, zpool_hdl, B_FALSE, errbuf)) == NULL) {
|
||||
zpool_close(zpool_hdl);
|
||||
return (-1);
|
||||
}
|
||||
@@ -4223,6 +4325,18 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
|
||||
"a child dataset already has a snapshot "
|
||||
"with the new name"));
|
||||
(void) zfs_error(hdl, EZFS_EXISTS, errbuf);
|
||||
} else if (errno == EACCES) {
|
||||
if (zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) ==
|
||||
ZIO_CRYPT_OFF) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"cannot rename an unencrypted dataset to "
|
||||
"be a decendent of an encrypted one"));
|
||||
} else {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"cannot move encryption child outside of "
|
||||
"its encryption root"));
|
||||
}
|
||||
(void) zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf);
|
||||
} else {
|
||||
(void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
|
||||
}
|
||||
|
||||
@@ -109,6 +109,11 @@ get_stats_for_obj(differ_info_t *di, const char *dsname, uint64_t obj,
|
||||
"The sys_config privilege or diff delegated permission "
|
||||
"is needed\nto discover path names"));
|
||||
return (-1);
|
||||
} else if (di->zerr == EACCES) {
|
||||
(void) snprintf(di->errbuf, sizeof (di->errbuf),
|
||||
dgettext(TEXT_DOMAIN,
|
||||
"Key must be loaded to discover path names"));
|
||||
return (-1);
|
||||
} else {
|
||||
(void) snprintf(di->errbuf, sizeof (di->errbuf),
|
||||
dgettext(TEXT_DOMAIN,
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/vfs.h>
|
||||
#include <sys/dsl_crypt.h>
|
||||
|
||||
#include <libzfs.h>
|
||||
|
||||
@@ -465,6 +466,7 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
|
||||
char mntopts[MNT_LINE_MAX];
|
||||
char overlay[ZFS_MAXPROPLEN];
|
||||
libzfs_handle_t *hdl = zhp->zfs_hdl;
|
||||
uint64_t keystatus;
|
||||
int remount = 0, rc;
|
||||
|
||||
if (options == NULL) {
|
||||
@@ -501,6 +503,39 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
|
||||
mountpoint));
|
||||
}
|
||||
|
||||
/*
|
||||
* If the filesystem is encrypted the key must be loaded in order to
|
||||
* mount. If the key isn't loaded, the MS_CRYPT flag decides whether
|
||||
* or not we attempt to load the keys. Note: we must call
|
||||
* zfs_refresh_properties() here since some callers of this function
|
||||
* (most notably zpool_enable_datasets()) may implicitly load our key
|
||||
* by loading the parent's key first.
|
||||
*/
|
||||
if (zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) != ZIO_CRYPT_OFF) {
|
||||
zfs_refresh_properties(zhp);
|
||||
keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS);
|
||||
|
||||
/*
|
||||
* If the key is unavailable and MS_CRYPT is set give the
|
||||
* user a chance to enter the key. Otherwise just fail
|
||||
* immediately.
|
||||
*/
|
||||
if (keystatus == ZFS_KEYSTATUS_UNAVAILABLE) {
|
||||
if (flags & MS_CRYPT) {
|
||||
rc = zfs_crypto_load_key(zhp, B_FALSE, NULL);
|
||||
if (rc)
|
||||
return (rc);
|
||||
} else {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"encryption key not loaded"));
|
||||
return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED,
|
||||
dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
|
||||
mountpoint));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Append zfsutil option so the mount helper allow the mount
|
||||
*/
|
||||
@@ -1136,6 +1171,12 @@ mount_cb(zfs_handle_t *zhp, void *data)
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS) ==
|
||||
ZFS_KEYSTATUS_UNAVAILABLE) {
|
||||
zfs_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* If this filesystem is inconsistent and has a receive resume
|
||||
* token, we can not mount it.
|
||||
@@ -1225,6 +1266,14 @@ zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags)
|
||||
|
||||
ret = 0;
|
||||
for (i = 0; i < cb.cb_used; i++) {
|
||||
/*
|
||||
* don't attempt to mount encrypted datasets with
|
||||
* unloaded keys
|
||||
*/
|
||||
if (zfs_prop_get_int(cb.cb_handles[i], ZFS_PROP_KEYSTATUS) ==
|
||||
ZFS_KEYSTATUS_UNAVAILABLE)
|
||||
continue;
|
||||
|
||||
if (zfs_mount(cb.cb_handles[i], mntopts, flags) != 0)
|
||||
ret = -1;
|
||||
else
|
||||
|
||||
@@ -1160,6 +1160,9 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
|
||||
zfs_cmd_t zc = {"\0"};
|
||||
nvlist_t *zc_fsprops = NULL;
|
||||
nvlist_t *zc_props = NULL;
|
||||
nvlist_t *hidden_args = NULL;
|
||||
uint8_t *wkeydata = NULL;
|
||||
uint_t wkeylen = 0;
|
||||
char msg[1024];
|
||||
int ret = -1;
|
||||
|
||||
@@ -1190,17 +1193,34 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
|
||||
strcmp(zonestr, "on") == 0);
|
||||
|
||||
if ((zc_fsprops = zfs_valid_proplist(hdl, ZFS_TYPE_FILESYSTEM,
|
||||
fsprops, zoned, NULL, NULL, msg)) == NULL) {
|
||||
fsprops, zoned, NULL, NULL, B_TRUE, msg)) == NULL) {
|
||||
goto create_failed;
|
||||
}
|
||||
if (!zc_props &&
|
||||
(nvlist_alloc(&zc_props, NV_UNIQUE_NAME, 0) != 0)) {
|
||||
goto create_failed;
|
||||
}
|
||||
if (zfs_crypto_create(hdl, NULL, zc_fsprops, props,
|
||||
&wkeydata, &wkeylen) != 0) {
|
||||
zfs_error(hdl, EZFS_CRYPTOFAILED, msg);
|
||||
goto create_failed;
|
||||
}
|
||||
if (nvlist_add_nvlist(zc_props,
|
||||
ZPOOL_ROOTFS_PROPS, zc_fsprops) != 0) {
|
||||
goto create_failed;
|
||||
}
|
||||
if (wkeydata != NULL) {
|
||||
if (nvlist_alloc(&hidden_args, NV_UNIQUE_NAME, 0) != 0)
|
||||
goto create_failed;
|
||||
|
||||
if (nvlist_add_uint8_array(hidden_args, "wkeydata",
|
||||
wkeydata, wkeylen) != 0)
|
||||
goto create_failed;
|
||||
|
||||
if (nvlist_add_nvlist(zc_props, ZPOOL_HIDDEN_ARGS,
|
||||
hidden_args) != 0)
|
||||
goto create_failed;
|
||||
}
|
||||
}
|
||||
|
||||
if (zc_props && zcmd_write_src_nvlist(hdl, &zc, zc_props) != 0)
|
||||
@@ -1213,6 +1233,9 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
|
||||
zcmd_free_nvlists(&zc);
|
||||
nvlist_free(zc_props);
|
||||
nvlist_free(zc_fsprops);
|
||||
nvlist_free(hidden_args);
|
||||
if (wkeydata != NULL)
|
||||
free(wkeydata);
|
||||
|
||||
switch (errno) {
|
||||
case EBUSY:
|
||||
@@ -1282,6 +1305,9 @@ create_failed:
|
||||
zcmd_free_nvlists(&zc);
|
||||
nvlist_free(zc_props);
|
||||
nvlist_free(zc_fsprops);
|
||||
nvlist_free(hidden_args);
|
||||
if (wkeydata != NULL)
|
||||
free(wkeydata);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
||||
+455
-44
@@ -61,6 +61,7 @@
|
||||
#include "libzfs_impl.h"
|
||||
#include <zlib.h>
|
||||
#include <sys/zio_checksum.h>
|
||||
#include <sys/dsl_crypt.h>
|
||||
#include <sys/ddt.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sha2.h>
|
||||
@@ -336,11 +337,9 @@ cksummer(void *arg)
|
||||
struct drr_object *drro = &drr->drr_u.drr_object;
|
||||
if (drro->drr_bonuslen > 0) {
|
||||
(void) ssread(buf,
|
||||
P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8),
|
||||
ofp);
|
||||
DRR_OBJECT_PAYLOAD_SIZE(drro), ofp);
|
||||
}
|
||||
if (dump_record(drr, buf,
|
||||
P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8),
|
||||
if (dump_record(drr, buf, DRR_OBJECT_PAYLOAD_SIZE(drro),
|
||||
&stream_cksum, outfd) != 0)
|
||||
goto out;
|
||||
break;
|
||||
@@ -349,8 +348,8 @@ cksummer(void *arg)
|
||||
case DRR_SPILL:
|
||||
{
|
||||
struct drr_spill *drrs = &drr->drr_u.drr_spill;
|
||||
(void) ssread(buf, drrs->drr_length, ofp);
|
||||
if (dump_record(drr, buf, drrs->drr_length,
|
||||
(void) ssread(buf, DRR_SPILL_PAYLOAD_SIZE(drrs), ofp);
|
||||
if (dump_record(drr, buf, DRR_SPILL_PAYLOAD_SIZE(drrs),
|
||||
&stream_cksum, outfd) != 0)
|
||||
goto out;
|
||||
break;
|
||||
@@ -380,7 +379,7 @@ cksummer(void *arg)
|
||||
|
||||
if (ZIO_CHECKSUM_EQUAL(drrw->drr_key.ddk_cksum,
|
||||
zero_cksum) ||
|
||||
!DRR_IS_DEDUP_CAPABLE(drrw->drr_checksumflags)) {
|
||||
!DRR_IS_DEDUP_CAPABLE(drrw->drr_flags)) {
|
||||
SHA2_CTX ctx;
|
||||
zio_cksum_t tmpsha256;
|
||||
|
||||
@@ -397,7 +396,7 @@ cksummer(void *arg)
|
||||
drrw->drr_key.ddk_cksum.zc_word[3] =
|
||||
BE_64(tmpsha256.zc_word[3]);
|
||||
drrw->drr_checksumtype = ZIO_CHECKSUM_SHA256;
|
||||
drrw->drr_checksumflags = DRR_CHECKSUM_DEDUP;
|
||||
drrw->drr_flags |= DRR_CHECKSUM_DEDUP;
|
||||
}
|
||||
|
||||
dataref.ref_guid = drrw->drr_toguid;
|
||||
@@ -426,8 +425,7 @@ cksummer(void *arg)
|
||||
|
||||
wbr_drrr->drr_checksumtype =
|
||||
drrw->drr_checksumtype;
|
||||
wbr_drrr->drr_checksumflags =
|
||||
drrw->drr_checksumflags;
|
||||
wbr_drrr->drr_flags = drrw->drr_flags;
|
||||
wbr_drrr->drr_key.ddk_cksum =
|
||||
drrw->drr_key.ddk_cksum;
|
||||
wbr_drrr->drr_key.ddk_prop =
|
||||
@@ -466,6 +464,14 @@ cksummer(void *arg)
|
||||
break;
|
||||
}
|
||||
|
||||
case DRR_OBJECT_RANGE:
|
||||
{
|
||||
if (dump_record(drr, NULL, 0, &stream_cksum,
|
||||
outfd) != 0)
|
||||
goto out;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
(void) fprintf(stderr, "INVALID record type 0x%x\n",
|
||||
drr->drr_type);
|
||||
@@ -614,6 +620,7 @@ typedef struct send_data {
|
||||
const char *fsname;
|
||||
const char *fromsnap;
|
||||
const char *tosnap;
|
||||
boolean_t raw;
|
||||
boolean_t recursive;
|
||||
boolean_t verbose;
|
||||
boolean_t seenfrom;
|
||||
@@ -635,6 +642,7 @@ typedef struct send_data {
|
||||
* "snapprops" -> { name (lastname) -> { name -> value } }
|
||||
*
|
||||
* "origin" -> number (guid) (if clone)
|
||||
* "is_encroot" -> boolean
|
||||
* "sent" -> boolean (not on-disk)
|
||||
* }
|
||||
* }
|
||||
@@ -812,7 +820,7 @@ static int
|
||||
send_iterate_fs(zfs_handle_t *zhp, void *arg)
|
||||
{
|
||||
send_data_t *sd = arg;
|
||||
nvlist_t *nvfs, *nv;
|
||||
nvlist_t *nvfs = NULL, *nv = NULL;
|
||||
int rv = 0;
|
||||
uint64_t parent_fromsnap_guid_save = sd->parent_fromsnap_guid;
|
||||
uint64_t fromsnap_txg_save = sd->fromsnap_txg;
|
||||
@@ -878,8 +886,37 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg)
|
||||
/* iterate over props */
|
||||
VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
|
||||
send_iterate_prop(zhp, nv);
|
||||
|
||||
if (zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) != ZIO_CRYPT_OFF) {
|
||||
boolean_t encroot;
|
||||
|
||||
/* determine if this dataset is an encryption root */
|
||||
if (zfs_crypto_get_encryption_root(zhp, &encroot, NULL) != 0) {
|
||||
rv = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (encroot)
|
||||
VERIFY(0 == nvlist_add_boolean(nvfs, "is_encroot"));
|
||||
|
||||
/*
|
||||
* Encrypted datasets can only be sent with properties if
|
||||
* the raw flag is specified because the receive side doesn't
|
||||
* currently have a mechanism for recursively asking the user
|
||||
* for new encryption parameters.
|
||||
*/
|
||||
if (!sd->raw) {
|
||||
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
|
||||
"cannot send %s@%s: encrypted dataset %s may not "
|
||||
"be sent with properties without the raw flag\n"),
|
||||
sd->fsname, sd->tosnap, zhp->zfs_name);
|
||||
rv = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
VERIFY(0 == nvlist_add_nvlist(nvfs, "props", nv));
|
||||
nvlist_free(nv);
|
||||
|
||||
/* iterate over snaps, and set sd->parent_fromsnap_guid */
|
||||
sd->parent_fromsnap_guid = 0;
|
||||
@@ -895,7 +932,6 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg)
|
||||
(void) snprintf(guidstring, sizeof (guidstring),
|
||||
"0x%llx", (longlong_t)guid);
|
||||
VERIFY(0 == nvlist_add_nvlist(sd->fss, guidstring, nvfs));
|
||||
nvlist_free(nvfs);
|
||||
|
||||
/* iterate over children */
|
||||
if (sd->recursive)
|
||||
@@ -905,6 +941,8 @@ out:
|
||||
sd->parent_fromsnap_guid = parent_fromsnap_guid_save;
|
||||
sd->fromsnap_txg = fromsnap_txg_save;
|
||||
sd->tosnap_txg = tosnap_txg_save;
|
||||
nvlist_free(nv);
|
||||
nvlist_free(nvfs);
|
||||
|
||||
zfs_close(zhp);
|
||||
return (rv);
|
||||
@@ -912,7 +950,7 @@ out:
|
||||
|
||||
static int
|
||||
gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
|
||||
const char *tosnap, boolean_t recursive, boolean_t verbose,
|
||||
const char *tosnap, boolean_t recursive, boolean_t raw, boolean_t verbose,
|
||||
nvlist_t **nvlp, avl_tree_t **avlp)
|
||||
{
|
||||
zfs_handle_t *zhp;
|
||||
@@ -928,6 +966,7 @@ gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
|
||||
sd.fromsnap = fromsnap;
|
||||
sd.tosnap = tosnap;
|
||||
sd.recursive = recursive;
|
||||
sd.raw = raw;
|
||||
sd.verbose = verbose;
|
||||
|
||||
if ((error = send_iterate_fs(zhp, &sd)) != 0) {
|
||||
@@ -959,7 +998,7 @@ typedef struct send_dump_data {
|
||||
uint64_t prevsnap_obj;
|
||||
boolean_t seenfrom, seento, replicate, doall, fromorigin;
|
||||
boolean_t verbose, dryrun, parsable, progress, embed_data, std_out;
|
||||
boolean_t large_block, compress;
|
||||
boolean_t large_block, compress, raw;
|
||||
int outfd;
|
||||
boolean_t err;
|
||||
nvlist_t *fss;
|
||||
@@ -1081,6 +1120,11 @@ dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
|
||||
"not an earlier snapshot from the same fs"));
|
||||
return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
|
||||
|
||||
case EACCES:
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"source key must be loaded"));
|
||||
return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));
|
||||
|
||||
case ENOENT:
|
||||
if (zfs_dataset_exists(hdl, zc.zc_name,
|
||||
ZFS_TYPE_SNAPSHOT)) {
|
||||
@@ -1263,6 +1307,8 @@ dump_snapshot(zfs_handle_t *zhp, void *arg)
|
||||
flags |= LZC_SEND_FLAG_EMBED_DATA;
|
||||
if (sdd->compress)
|
||||
flags |= LZC_SEND_FLAG_COMPRESS;
|
||||
if (sdd->raw)
|
||||
flags |= LZC_SEND_FLAG_RAW;
|
||||
|
||||
if (!sdd->doall && !isfromsnap && !istosnap) {
|
||||
if (sdd->replicate) {
|
||||
@@ -1646,6 +1692,8 @@ zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd,
|
||||
lzc_flags |= LZC_SEND_FLAG_EMBED_DATA;
|
||||
if (flags->compress || nvlist_exists(resume_nvl, "compressok"))
|
||||
lzc_flags |= LZC_SEND_FLAG_COMPRESS;
|
||||
if (flags->raw || nvlist_exists(resume_nvl, "rawok"))
|
||||
lzc_flags |= LZC_SEND_FLAG_RAW;
|
||||
|
||||
if (guid_to_name(hdl, toname, toguid, B_FALSE, name) != 0) {
|
||||
if (zfs_dataset_exists(hdl, toname, ZFS_TYPE_DATASET)) {
|
||||
@@ -1723,6 +1771,11 @@ zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd,
|
||||
switch (error) {
|
||||
case 0:
|
||||
return (0);
|
||||
case EACCES:
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"source key must be loaded"));
|
||||
return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));
|
||||
|
||||
case EXDEV:
|
||||
case ENOENT:
|
||||
case EDQUOT:
|
||||
@@ -1801,7 +1854,14 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
|
||||
}
|
||||
}
|
||||
|
||||
if (flags->dedup && !flags->dryrun) {
|
||||
/*
|
||||
* Start the dedup thread if this is a dedup stream. We do not bother
|
||||
* doing this if this a raw send of an encrypted dataset with dedup off
|
||||
* because normal encrypted blocks won't dedup.
|
||||
*/
|
||||
if (flags->dedup && !flags->dryrun && !(flags->raw &&
|
||||
zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) != ZIO_CRYPT_OFF &&
|
||||
zfs_prop_get_int(zhp, ZFS_PROP_DEDUP) == ZIO_CHECKSUM_OFF)) {
|
||||
featureflags |= (DMU_BACKUP_FEATURE_DEDUP |
|
||||
DMU_BACKUP_FEATURE_DEDUPPROPS);
|
||||
if ((err = socketpair(AF_UNIX, SOCK_STREAM, 0, pipefd)) != 0) {
|
||||
@@ -1842,10 +1902,13 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
|
||||
VERIFY(0 == nvlist_add_boolean(hdrnv,
|
||||
"not_recursive"));
|
||||
}
|
||||
if (flags->raw) {
|
||||
VERIFY(0 == nvlist_add_boolean(hdrnv, "raw"));
|
||||
}
|
||||
|
||||
err = gather_nvlist(zhp->zfs_hdl, zhp->zfs_name,
|
||||
fromsnap, tosnap, flags->replicate, flags->verbose,
|
||||
&fss, &fsavl);
|
||||
fromsnap, tosnap, flags->replicate, flags->raw,
|
||||
flags->verbose, &fss, &fsavl);
|
||||
if (err)
|
||||
goto err_out;
|
||||
VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss));
|
||||
@@ -1914,6 +1977,7 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
|
||||
sdd.large_block = flags->largeblock;
|
||||
sdd.embed_data = flags->embed_data;
|
||||
sdd.compress = flags->compress;
|
||||
sdd.raw = flags->raw;
|
||||
sdd.filter_cb = filter_func;
|
||||
sdd.filter_cb_arg = cb_arg;
|
||||
if (debugnvp)
|
||||
@@ -2075,6 +2139,11 @@ zfs_send_one(zfs_handle_t *zhp, const char *from, int fd,
|
||||
}
|
||||
return (zfs_error(hdl, EZFS_NOENT, errbuf));
|
||||
|
||||
case EACCES:
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"dataset key must be loaded"));
|
||||
return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));
|
||||
|
||||
case EBUSY:
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"target is busy; if a filesystem, "
|
||||
@@ -2165,6 +2234,63 @@ recv_read_nvlist(libzfs_handle_t *hdl, int fd, int len, nvlist_t **nvp,
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the grand origin (origin of origin of origin...) of a given handle.
|
||||
* If this dataset is not a clone, it simply returns a copy of the original
|
||||
* handle.
|
||||
*/
|
||||
static zfs_handle_t *
|
||||
recv_open_grand_origin(zfs_handle_t *zhp)
|
||||
{
|
||||
char origin[ZFS_MAX_DATASET_NAME_LEN];
|
||||
zprop_source_t src;
|
||||
zfs_handle_t *ozhp = zfs_handle_dup(zhp);
|
||||
|
||||
while (ozhp != NULL) {
|
||||
if (zfs_prop_get(ozhp, ZFS_PROP_ORIGIN, origin,
|
||||
sizeof (origin), &src, NULL, 0, B_FALSE) != 0)
|
||||
break;
|
||||
|
||||
(void) zfs_close(ozhp);
|
||||
ozhp = zfs_open(zhp->zfs_hdl, origin, ZFS_TYPE_FILESYSTEM);
|
||||
}
|
||||
|
||||
return (ozhp);
|
||||
}
|
||||
|
||||
static int
|
||||
recv_rename_impl(zfs_handle_t *zhp, zfs_cmd_t *zc)
|
||||
{
|
||||
int err;
|
||||
zfs_handle_t *ozhp = NULL;
|
||||
|
||||
/*
|
||||
* Attempt to rename the dataset. If it fails with EACCES we have
|
||||
* attempted to rename the dataset outside of its encryption root.
|
||||
* Force the dataset to become an encryption root and try again.
|
||||
*/
|
||||
err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_RENAME, &zc);
|
||||
if (err == EACCES) {
|
||||
ozhp = recv_open_grand_origin(zhp);
|
||||
if (ozhp == NULL) {
|
||||
err = ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = lzc_change_key(ozhp->zfs_name, DCP_CMD_FORCE_NEW_KEY,
|
||||
NULL, NULL, 0);
|
||||
if (err != 0)
|
||||
goto out;
|
||||
|
||||
err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_RENAME, &zc);
|
||||
}
|
||||
|
||||
out:
|
||||
if (ozhp != NULL)
|
||||
zfs_close(ozhp);
|
||||
return (err);
|
||||
}
|
||||
|
||||
static int
|
||||
recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
|
||||
int baselen, char *newname, recvflags_t *flags)
|
||||
@@ -2172,20 +2298,23 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
|
||||
static int seq;
|
||||
zfs_cmd_t zc = {"\0"};
|
||||
int err;
|
||||
prop_changelist_t *clp;
|
||||
zfs_handle_t *zhp;
|
||||
prop_changelist_t *clp = NULL;
|
||||
zfs_handle_t *zhp = NULL;
|
||||
|
||||
zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
|
||||
if (zhp == NULL)
|
||||
return (-1);
|
||||
if (zhp == NULL) {
|
||||
err = -1;
|
||||
goto out;
|
||||
}
|
||||
clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
|
||||
flags->force ? MS_FORCE : 0);
|
||||
zfs_close(zhp);
|
||||
if (clp == NULL)
|
||||
return (-1);
|
||||
if (clp == NULL) {
|
||||
err = -1;
|
||||
goto out;
|
||||
}
|
||||
err = changelist_prefix(clp);
|
||||
if (err)
|
||||
return (err);
|
||||
goto out;
|
||||
|
||||
zc.zc_objset_type = DMU_OST_ZFS;
|
||||
(void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
|
||||
@@ -2199,7 +2328,7 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
|
||||
(void) printf("attempting rename %s to %s\n",
|
||||
zc.zc_name, zc.zc_value);
|
||||
}
|
||||
err = ioctl(hdl->libzfs_fd, ZFS_IOC_RENAME, &zc);
|
||||
err = recv_rename_impl(zhp, &zc);
|
||||
if (err == 0)
|
||||
changelist_rename(clp, name, tryname);
|
||||
} else {
|
||||
@@ -2217,7 +2346,7 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
|
||||
(void) printf("failed - trying rename %s to %s\n",
|
||||
zc.zc_name, zc.zc_value);
|
||||
}
|
||||
err = ioctl(hdl->libzfs_fd, ZFS_IOC_RENAME, &zc);
|
||||
err = recv_rename_impl(zhp, &zc);
|
||||
if (err == 0)
|
||||
changelist_rename(clp, name, newname);
|
||||
if (err && flags->verbose) {
|
||||
@@ -2233,7 +2362,62 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
|
||||
}
|
||||
|
||||
(void) changelist_postfix(clp);
|
||||
changelist_free(clp);
|
||||
|
||||
out:
|
||||
if (clp != NULL)
|
||||
changelist_free(clp);
|
||||
if (zhp != NULL)
|
||||
zfs_close(zhp);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
static int
|
||||
recv_promote(libzfs_handle_t *hdl, const char *fsname,
|
||||
const char *origin_fsname, recvflags_t *flags)
|
||||
{
|
||||
int err;
|
||||
zfs_cmd_t zc = {"\0"};
|
||||
zfs_handle_t *zhp = NULL, *ozhp = NULL;
|
||||
|
||||
if (flags->verbose)
|
||||
(void) printf("promoting %s\n", fsname);
|
||||
|
||||
(void) strlcpy(zc.zc_value, origin_fsname, sizeof (zc.zc_value));
|
||||
(void) strlcpy(zc.zc_name, fsname, sizeof (zc.zc_name));
|
||||
|
||||
/*
|
||||
* Attempt to promote the dataset. If it fails with EACCES the
|
||||
* promotion would cause this dataset to leave its encryption root.
|
||||
* Force the origin to become an encryption root and try again.
|
||||
*/
|
||||
err = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
|
||||
if (err == EACCES) {
|
||||
zhp = zfs_open(hdl, fsname, ZFS_TYPE_DATASET);
|
||||
if (zhp == NULL) {
|
||||
err = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ozhp = recv_open_grand_origin(zhp);
|
||||
if (ozhp == NULL) {
|
||||
err = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = lzc_change_key(ozhp->zfs_name, DCP_CMD_FORCE_NEW_KEY,
|
||||
NULL, NULL, 0);
|
||||
if (err != 0)
|
||||
goto out;
|
||||
|
||||
err = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
|
||||
}
|
||||
|
||||
out:
|
||||
if (zhp != NULL)
|
||||
zfs_close(zhp);
|
||||
if (ozhp != NULL)
|
||||
zfs_close(ozhp);
|
||||
|
||||
return (err);
|
||||
}
|
||||
@@ -2435,6 +2619,140 @@ created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
|
||||
return (rv);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function reestablishes the heirarchy of encryption roots after a
|
||||
* recursive incremental receive has completed. This must be done after the
|
||||
* second call to recv_incremental_replication() has renamed and promoted all
|
||||
* sent datasets to their final locations in the dataset heriarchy.
|
||||
*/
|
||||
static int
|
||||
recv_fix_encryption_heirarchy(libzfs_handle_t *hdl, const char *destname,
|
||||
nvlist_t *stream_nv, avl_tree_t *stream_avl)
|
||||
{
|
||||
int err;
|
||||
nvpair_t *fselem = NULL;
|
||||
nvlist_t *stream_fss;
|
||||
|
||||
VERIFY(0 == nvlist_lookup_nvlist(stream_nv, "fss", &stream_fss));
|
||||
|
||||
while ((fselem = nvlist_next_nvpair(stream_fss, fselem)) != NULL) {
|
||||
zfs_handle_t *zhp = NULL;
|
||||
uint64_t crypt;
|
||||
nvlist_t *snaps, *props, *stream_nvfs = NULL;
|
||||
nvpair_t *snapel = NULL;
|
||||
boolean_t is_encroot, is_clone, stream_encroot;
|
||||
char *cp;
|
||||
char *stream_keylocation = NULL;
|
||||
char keylocation[MAXNAMELEN];
|
||||
char fsname[ZFS_MAX_DATASET_NAME_LEN];
|
||||
|
||||
keylocation[0] = '\0';
|
||||
VERIFY(0 == nvpair_value_nvlist(fselem, &stream_nvfs));
|
||||
VERIFY(0 == nvlist_lookup_nvlist(stream_nvfs, "snaps", &snaps));
|
||||
VERIFY(0 == nvlist_lookup_nvlist(stream_nvfs, "props", &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;
|
||||
|
||||
VERIFY(0 == nvpair_value_uint64(snapel, &guid));
|
||||
err = guid_to_name(hdl, destname, guid, B_FALSE,
|
||||
fsname);
|
||||
if (err == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (err != 0)
|
||||
continue;
|
||||
|
||||
cp = strchr(fsname, '@');
|
||||
if (cp != NULL)
|
||||
*cp = '\0';
|
||||
|
||||
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 filesystems */
|
||||
if (crypt == ZIO_CRYPT_OFF) {
|
||||
zfs_close(zhp);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the dataset is flagged as an encryption root, was not
|
||||
* received as a clone and is not currently an encryption root,
|
||||
* force it to become one. Fixup the keylocation if necessary.
|
||||
*/
|
||||
if (stream_encroot) {
|
||||
if (!is_clone && !is_encroot) {
|
||||
err = lzc_change_key(fsname,
|
||||
DCP_CMD_FORCE_NEW_KEY, NULL, NULL, 0);
|
||||
if (err != 0) {
|
||||
zfs_close(zhp);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
VERIFY(0 == nvlist_lookup_string(props,
|
||||
zfs_prop_to_name(ZFS_PROP_KEYLOCATION),
|
||||
&stream_keylocation));
|
||||
|
||||
/*
|
||||
* Refresh the properties in case the call to
|
||||
* lzc_change_key() changed the value.
|
||||
*/
|
||||
zfs_refresh_properties(zhp);
|
||||
err = zfs_prop_get(zhp, ZFS_PROP_KEYLOCATION,
|
||||
keylocation, sizeof (keylocation), NULL, NULL,
|
||||
0, B_TRUE);
|
||||
if (err != 0) {
|
||||
zfs_close(zhp);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (strcmp(keylocation, stream_keylocation) != 0) {
|
||||
err = zfs_prop_set(zhp,
|
||||
zfs_prop_to_name(ZFS_PROP_KEYLOCATION),
|
||||
stream_keylocation);
|
||||
if (err != 0) {
|
||||
zfs_close(zhp);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the dataset is not flagged as an encryption root and is
|
||||
* currently an encryption root, force it to inherit from its
|
||||
* parent.
|
||||
*/
|
||||
if (!stream_encroot && is_encroot) {
|
||||
err = lzc_change_key(fsname, DCP_CMD_FORCE_INHERIT,
|
||||
NULL, NULL, 0);
|
||||
if (err != 0) {
|
||||
zfs_close(zhp);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
zfs_close(zhp);
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
||||
error:
|
||||
return (err);
|
||||
}
|
||||
|
||||
static int
|
||||
recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs,
|
||||
recvflags_t *flags, nvlist_t *stream_nv, avl_tree_t *stream_avl,
|
||||
@@ -2464,7 +2782,7 @@ again:
|
||||
VERIFY(0 == nvlist_alloc(&deleted, NV_UNIQUE_NAME, 0));
|
||||
|
||||
if ((error = gather_nvlist(hdl, tofs, fromsnap, NULL,
|
||||
recursive, B_FALSE, &local_nv, &local_avl)) != 0)
|
||||
recursive, B_TRUE, B_FALSE, &local_nv, &local_avl)) != 0)
|
||||
return (error);
|
||||
|
||||
/*
|
||||
@@ -2513,22 +2831,15 @@ again:
|
||||
stream_originguid, originguid)) {
|
||||
case 1: {
|
||||
/* promote it! */
|
||||
zfs_cmd_t zc = {"\0"};
|
||||
nvlist_t *origin_nvfs;
|
||||
char *origin_fsname;
|
||||
|
||||
if (flags->verbose)
|
||||
(void) printf("promoting %s\n", fsname);
|
||||
|
||||
origin_nvfs = fsavl_find(local_avl, originguid,
|
||||
NULL);
|
||||
VERIFY(0 == nvlist_lookup_string(origin_nvfs,
|
||||
"name", &origin_fsname));
|
||||
(void) strlcpy(zc.zc_value, origin_fsname,
|
||||
sizeof (zc.zc_value));
|
||||
(void) strlcpy(zc.zc_name, fsname,
|
||||
sizeof (zc.zc_name));
|
||||
error = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
|
||||
error = recv_promote(hdl, fsname, origin_fsname,
|
||||
flags);
|
||||
if (error == 0)
|
||||
progress = B_TRUE;
|
||||
break;
|
||||
@@ -2744,7 +3055,7 @@ doagain:
|
||||
goto again;
|
||||
}
|
||||
|
||||
return (needagain);
|
||||
return (needagain || error != 0);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -2765,7 +3076,7 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
|
||||
int error;
|
||||
boolean_t anyerr = B_FALSE;
|
||||
boolean_t softerr = B_FALSE;
|
||||
boolean_t recursive;
|
||||
boolean_t recursive, raw;
|
||||
|
||||
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
||||
"cannot receive"));
|
||||
@@ -2789,6 +3100,7 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
|
||||
|
||||
recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
|
||||
ENOENT);
|
||||
raw = (nvlist_lookup_boolean(stream_nv, "raw") == 0);
|
||||
|
||||
if (recursive && strchr(destname, '@')) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
@@ -2944,6 +3256,11 @@ 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_heirarchy(hdl, destname,
|
||||
stream_nv, stream_avl);
|
||||
}
|
||||
|
||||
out:
|
||||
fsavl_destroy(stream_avl);
|
||||
nvlist_free(stream_nv);
|
||||
@@ -3194,7 +3511,7 @@ zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type, boolean_t zoned,
|
||||
if (toplevel) {
|
||||
/* convert override strings properties to native */
|
||||
if ((voprops = zfs_valid_proplist(hdl, ZFS_TYPE_DATASET,
|
||||
oprops, zoned, zhp, zpool_hdl, errbuf)) == NULL) {
|
||||
oprops, zoned, zhp, zpool_hdl, B_FALSE, errbuf)) == NULL) {
|
||||
ret = zfs_error(hdl, EZFS_BADPROP, errbuf);
|
||||
goto error;
|
||||
}
|
||||
@@ -3247,6 +3564,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
char destsnap[MAXPATHLEN * 2];
|
||||
char origin[MAXNAMELEN];
|
||||
char name[MAXPATHLEN];
|
||||
char tmp_keylocation[MAXNAMELEN];
|
||||
nvlist_t *rcvprops = NULL; /* props received from the send stream */
|
||||
nvlist_t *oxprops = NULL; /* override (-o) and exclude (-x) props */
|
||||
nvlist_t *origprops = NULL; /* original props (if destination exists) */
|
||||
@@ -3256,6 +3574,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
|
||||
begin_time = time(NULL);
|
||||
bzero(origin, MAXNAMELEN);
|
||||
bzero(tmp_keylocation, MAXNAMELEN);
|
||||
|
||||
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
||||
"cannot receive"));
|
||||
@@ -3264,6 +3583,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
ENOENT);
|
||||
|
||||
if (stream_avl != NULL) {
|
||||
char *keylocation = NULL;
|
||||
nvlist_t *lookup = NULL;
|
||||
nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
|
||||
&snapname);
|
||||
@@ -3276,6 +3596,22 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
newprops = B_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* The keylocation property may only be set on encryption roots,
|
||||
* but this dataset might not become an encryption root until
|
||||
* recv_fix_encryption_heirarchy() is called. That function
|
||||
* will fixup the keylocation anyway, so we temporarily unset
|
||||
* the keylocation for now to avoid any errors from the receive
|
||||
* ioctl.
|
||||
*/
|
||||
err = nvlist_lookup_string(rcvprops,
|
||||
zfs_prop_to_name(ZFS_PROP_KEYLOCATION), &keylocation);
|
||||
if (err == 0) {
|
||||
strcpy(tmp_keylocation, keylocation);
|
||||
(void) nvlist_remove_all(rcvprops,
|
||||
zfs_prop_to_name(ZFS_PROP_KEYLOCATION));
|
||||
}
|
||||
|
||||
if (flags->canmountoff) {
|
||||
VERIFY(0 == nvlist_add_uint64(rcvprops,
|
||||
zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0));
|
||||
@@ -3397,6 +3733,8 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
|
||||
boolean_t resuming = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo) &
|
||||
DMU_BACKUP_FEATURE_RESUMING;
|
||||
boolean_t raw = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo) &
|
||||
DMU_BACKUP_FEATURE_RAW;
|
||||
stream_wantsnewfs = (drrb->drr_fromguid == 0 ||
|
||||
(drrb->drr_flags & DRR_FLAG_CLONE) || originsnap) && !resuming;
|
||||
|
||||
@@ -3503,6 +3841,26 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* zfs recv -F cant be used to blow away an existing
|
||||
* encrypted filesystem. This is because it would require
|
||||
* the dsl dir to point to the the new key (or lack of a
|
||||
* key) and the old key at the same time. The -F flag may
|
||||
* still be used for deleting intermediate snapshots that
|
||||
* would otherwise prevent the receive from working.
|
||||
*/
|
||||
if (stream_wantsnewfs && flags->force &&
|
||||
zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) !=
|
||||
ZIO_CRYPT_OFF) {
|
||||
zfs_close(zhp);
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"zfs receive -F cannot be used to "
|
||||
"destroy an encrypted filesystem"));
|
||||
err = zfs_error(hdl, EZFS_BADRESTORE, errbuf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
if (!flags->dryrun && zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&
|
||||
stream_wantsnewfs) {
|
||||
/* We can't do online recv in this case */
|
||||
@@ -3541,6 +3899,8 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
|
||||
zfs_close(zhp);
|
||||
} else {
|
||||
zfs_handle_t *zhp;
|
||||
|
||||
/*
|
||||
* Destination filesystem does not exist. Therefore we better
|
||||
* be creating a new filesystem (either from a full backup, or
|
||||
@@ -3569,7 +3929,39 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* It is invalid to receive a properties stream that was
|
||||
* unencrypted on the send side as a child of an encrypted
|
||||
* parent. Technically there is nothing preventing this, but
|
||||
* it would mean that the encryption=off property which is
|
||||
* 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.
|
||||
*/
|
||||
if (!raw && rcvprops != NULL) {
|
||||
uint64_t crypt;
|
||||
|
||||
zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
|
||||
if (zhp == NULL) {
|
||||
err = zfs_error(hdl, EZFS_BADRESTORE, errbuf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION);
|
||||
zfs_close(zhp);
|
||||
|
||||
if (crypt != ZIO_CRYPT_OFF) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"parent '%s' must not be encrypted to "
|
||||
"receive unenecrypted property"), name);
|
||||
err = zfs_error(hdl, EZFS_BADPROP, errbuf);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
newfs = B_TRUE;
|
||||
*cp = '/';
|
||||
}
|
||||
|
||||
if (flags->verbose) {
|
||||
@@ -3601,7 +3993,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
goto out;
|
||||
|
||||
err = ioctl_err = lzc_receive_with_cmdprops(destsnap, rcvprops, oxprops,
|
||||
origin, flags->force, flags->resumable, infd, drr_noswap,
|
||||
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;
|
||||
@@ -3672,7 +4064,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
* get a strange "does not exist" error message.
|
||||
*/
|
||||
*cp = '\0';
|
||||
if (gather_nvlist(hdl, destsnap, NULL, NULL, B_FALSE,
|
||||
if (gather_nvlist(hdl, destsnap, NULL, NULL, B_FALSE, B_TRUE,
|
||||
B_FALSE, &local_nv, &local_avl) == 0) {
|
||||
*cp = '@';
|
||||
fs = fsavl_find(local_avl, drrb->drr_toguid, NULL);
|
||||
@@ -3708,6 +4100,20 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
"since most recent snapshot"), name);
|
||||
(void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
|
||||
break;
|
||||
case EACCES:
|
||||
if (raw && stream_wantsnewfs) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"failed to create encryption key"));
|
||||
} else if (raw && !stream_wantsnewfs) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"encryption key does not match "
|
||||
"existing key"));
|
||||
} else {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"inherited key must be loaded"));
|
||||
}
|
||||
(void) zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf);
|
||||
break;
|
||||
case EEXIST:
|
||||
cp = strchr(destsnap, '@');
|
||||
if (newfs) {
|
||||
@@ -3816,6 +4222,11 @@ out:
|
||||
if (prop_errors != NULL)
|
||||
nvlist_free(prop_errors);
|
||||
|
||||
if (tmp_keylocation[0] != '\0') {
|
||||
VERIFY(0 == nvlist_add_string(rcvprops,
|
||||
zfs_prop_to_name(ZFS_PROP_KEYLOCATION), tmp_keylocation));
|
||||
}
|
||||
|
||||
if (newprops)
|
||||
nvlist_free(rcvprops);
|
||||
|
||||
|
||||
@@ -264,6 +264,8 @@ libzfs_error_description(libzfs_handle_t *hdl)
|
||||
case EZFS_ACTIVE_POOL:
|
||||
return (dgettext(TEXT_DOMAIN, "pool is imported on a "
|
||||
"different host"));
|
||||
case EZFS_CRYPTOFAILED:
|
||||
return (dgettext(TEXT_DOMAIN, "encryption failure"));
|
||||
case EZFS_UNKNOWN:
|
||||
return (dgettext(TEXT_DOMAIN, "unknown error"));
|
||||
default:
|
||||
|
||||
+110
-28
@@ -175,34 +175,49 @@ lzc_ioctl(zfs_ioc_t ioc, const char *name,
|
||||
}
|
||||
|
||||
out:
|
||||
fnvlist_pack_free(packed, size);
|
||||
if (packed != NULL)
|
||||
fnvlist_pack_free(packed, size);
|
||||
free((void *)(uintptr_t)zc.zc_nvlist_dst);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
lzc_create(const char *fsname, enum lzc_dataset_type type, nvlist_t *props)
|
||||
lzc_create(const char *fsname, enum lzc_dataset_type type, nvlist_t *props,
|
||||
uint8_t *wkeydata, uint_t wkeylen)
|
||||
{
|
||||
int error;
|
||||
nvlist_t *hidden_args = NULL;
|
||||
nvlist_t *args = fnvlist_alloc();
|
||||
|
||||
fnvlist_add_int32(args, "type", (dmu_objset_type_t)type);
|
||||
if (props != NULL)
|
||||
fnvlist_add_nvlist(args, "props", props);
|
||||
|
||||
if (wkeydata != NULL) {
|
||||
hidden_args = fnvlist_alloc();
|
||||
fnvlist_add_uint8_array(hidden_args, "wkeydata", wkeydata,
|
||||
wkeylen);
|
||||
fnvlist_add_nvlist(args, ZPOOL_HIDDEN_ARGS, hidden_args);
|
||||
}
|
||||
|
||||
error = lzc_ioctl(ZFS_IOC_CREATE, fsname, args, NULL);
|
||||
nvlist_free(hidden_args);
|
||||
nvlist_free(args);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
lzc_clone(const char *fsname, const char *origin,
|
||||
nvlist_t *props)
|
||||
lzc_clone(const char *fsname, const char *origin, nvlist_t *props)
|
||||
{
|
||||
int error;
|
||||
nvlist_t *hidden_args = NULL;
|
||||
nvlist_t *args = fnvlist_alloc();
|
||||
|
||||
fnvlist_add_string(args, "origin", origin);
|
||||
if (props != NULL)
|
||||
fnvlist_add_nvlist(args, "props", props);
|
||||
error = lzc_ioctl(ZFS_IOC_CLONE, fsname, args, NULL);
|
||||
nvlist_free(hidden_args);
|
||||
nvlist_free(args);
|
||||
return (error);
|
||||
}
|
||||
@@ -532,6 +547,8 @@ lzc_send_resume(const char *snapname, const char *from, int fd,
|
||||
fnvlist_add_boolean(args, "embedok");
|
||||
if (flags & LZC_SEND_FLAG_COMPRESS)
|
||||
fnvlist_add_boolean(args, "compressok");
|
||||
if (flags & LZC_SEND_FLAG_RAW)
|
||||
fnvlist_add_boolean(args, "rawok");
|
||||
if (resumeobj != 0 || resumeoff != 0) {
|
||||
fnvlist_add_uint64(args, "resume_object", resumeobj);
|
||||
fnvlist_add_uint64(args, "resume_offset", resumeoff);
|
||||
@@ -601,17 +618,17 @@ recv_read(int fd, void *buf, int ilen)
|
||||
}
|
||||
|
||||
/*
|
||||
* Linux adds ZFS_IOC_RECV_NEW for resumable streams and preserves the legacy
|
||||
* ZFS_IOC_RECV user/kernel interface. The new interface supports all stream
|
||||
* options but is currently only used for resumable streams. This way updated
|
||||
* user space utilities will interoperate with older kernel modules.
|
||||
* Linux adds ZFS_IOC_RECV_NEW for resumable and raw streams and preserves the
|
||||
* legacy ZFS_IOC_RECV user/kernel interface. The new interface supports all
|
||||
* stream options but is currently only used for resumable streams. This way
|
||||
* updated user space utilities will interoperate with older kernel modules.
|
||||
*
|
||||
* Non-Linux OpenZFS platforms have opted to modify the legacy interface.
|
||||
*/
|
||||
static int
|
||||
recv_impl(const char *snapname, nvlist_t *recvdprops, nvlist_t *localprops,
|
||||
const char *origin, boolean_t force, boolean_t resumable, int input_fd,
|
||||
const dmu_replay_record_t *begin_record, int cleanup_fd,
|
||||
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)
|
||||
{
|
||||
@@ -651,7 +668,7 @@ recv_impl(const char *snapname, nvlist_t *recvdprops, nvlist_t *localprops,
|
||||
drr = *begin_record;
|
||||
}
|
||||
|
||||
if (resumable) {
|
||||
if (resumable || raw) {
|
||||
nvlist_t *outnvl = NULL;
|
||||
nvlist_t *innvl = fnvlist_alloc();
|
||||
|
||||
@@ -792,10 +809,10 @@ recv_impl(const char *snapname, nvlist_t *recvdprops, nvlist_t *localprops,
|
||||
*/
|
||||
int
|
||||
lzc_receive(const char *snapname, nvlist_t *props, const char *origin,
|
||||
boolean_t force, int fd)
|
||||
boolean_t force, boolean_t raw, int fd)
|
||||
{
|
||||
return (recv_impl(snapname, props, NULL, origin, force, B_FALSE, fd,
|
||||
NULL, -1, NULL, NULL, NULL, NULL));
|
||||
return (recv_impl(snapname, props, NULL, origin, force, B_FALSE, raw,
|
||||
fd, NULL, -1, NULL, NULL, NULL, NULL));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -806,10 +823,10 @@ lzc_receive(const char *snapname, nvlist_t *props, const char *origin,
|
||||
*/
|
||||
int
|
||||
lzc_receive_resumable(const char *snapname, nvlist_t *props, const char *origin,
|
||||
boolean_t force, int fd)
|
||||
boolean_t force, boolean_t raw, int fd)
|
||||
{
|
||||
return (recv_impl(snapname, props, NULL, origin, force, B_TRUE, fd,
|
||||
NULL, -1, NULL, NULL, NULL, NULL));
|
||||
return (recv_impl(snapname, props, NULL, origin, force, B_TRUE, raw,
|
||||
fd, NULL, -1, NULL, NULL, NULL, NULL));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -825,13 +842,14 @@ lzc_receive_resumable(const char *snapname, nvlist_t *props, const char *origin,
|
||||
*/
|
||||
int
|
||||
lzc_receive_with_header(const char *snapname, nvlist_t *props,
|
||||
const char *origin, boolean_t force, boolean_t resumable, int fd,
|
||||
const dmu_replay_record_t *begin_record)
|
||||
const char *origin, boolean_t force, boolean_t resumable, boolean_t raw,
|
||||
int fd, const dmu_replay_record_t *begin_record)
|
||||
{
|
||||
if (begin_record == NULL)
|
||||
return (EINVAL);
|
||||
return (recv_impl(snapname, props, NULL, origin, force, resumable, fd,
|
||||
begin_record, -1, NULL, NULL, NULL, NULL));
|
||||
|
||||
return (recv_impl(snapname, props, NULL, origin, force, resumable, raw,
|
||||
fd, begin_record, -1, NULL, NULL, NULL, NULL));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -855,13 +873,13 @@ lzc_receive_with_header(const char *snapname, nvlist_t *props,
|
||||
* property. Callers are responsible for freeing this nvlist.
|
||||
*/
|
||||
int lzc_receive_one(const char *snapname, nvlist_t *props,
|
||||
const char *origin, boolean_t force, boolean_t resumable, int input_fd,
|
||||
const dmu_replay_record_t *begin_record, int cleanup_fd,
|
||||
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, NULL, origin, force, resumable,
|
||||
input_fd, begin_record, cleanup_fd, read_bytes, errflags,
|
||||
raw, input_fd, begin_record, cleanup_fd, read_bytes, errflags,
|
||||
action_handle, errors));
|
||||
}
|
||||
|
||||
@@ -875,12 +893,13 @@ int lzc_receive_one(const char *snapname, nvlist_t *props,
|
||||
*/
|
||||
int lzc_receive_with_cmdprops(const char *snapname, nvlist_t *props,
|
||||
nvlist_t *cmdprops, const char *origin, boolean_t force,
|
||||
boolean_t resumable, 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)
|
||||
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,
|
||||
input_fd, begin_record, cleanup_fd, read_bytes, errflags,
|
||||
raw, input_fd, begin_record, cleanup_fd, read_bytes, errflags,
|
||||
action_handle, errors));
|
||||
}
|
||||
|
||||
@@ -1027,3 +1046,66 @@ lzc_destroy_bookmarks(nvlist_t *bmarks, nvlist_t **errlist)
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs key management functions
|
||||
*
|
||||
* crypto_cmd should be a value from zfs_ioc_crypto_cmd_t. If the command
|
||||
* specifies to load or change a wrapping key, the key should be specified in
|
||||
* the hidden_args nvlist so that it is not logged
|
||||
*/
|
||||
int
|
||||
lzc_load_key(const char *fsname, boolean_t noop, uint8_t *wkeydata,
|
||||
uint_t wkeylen)
|
||||
{
|
||||
int error;
|
||||
nvlist_t *ioc_args;
|
||||
nvlist_t *hidden_args;
|
||||
|
||||
if (wkeydata == NULL)
|
||||
return (EINVAL);
|
||||
|
||||
ioc_args = fnvlist_alloc();
|
||||
hidden_args = fnvlist_alloc();
|
||||
fnvlist_add_uint8_array(hidden_args, "wkeydata", wkeydata, wkeylen);
|
||||
fnvlist_add_nvlist(ioc_args, ZPOOL_HIDDEN_ARGS, hidden_args);
|
||||
if (noop)
|
||||
fnvlist_add_boolean(ioc_args, "noop");
|
||||
error = lzc_ioctl(ZFS_IOC_LOAD_KEY, fsname, ioc_args, NULL);
|
||||
nvlist_free(hidden_args);
|
||||
nvlist_free(ioc_args);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
lzc_unload_key(const char *fsname)
|
||||
{
|
||||
return (lzc_ioctl(ZFS_IOC_UNLOAD_KEY, fsname, NULL, NULL));
|
||||
}
|
||||
|
||||
int
|
||||
lzc_change_key(const char *fsname, uint64_t crypt_cmd, nvlist_t *props,
|
||||
uint8_t *wkeydata, uint_t wkeylen)
|
||||
{
|
||||
int error;
|
||||
nvlist_t *ioc_args = fnvlist_alloc();
|
||||
nvlist_t *hidden_args = NULL;
|
||||
|
||||
fnvlist_add_uint64(ioc_args, "crypt_cmd", crypt_cmd);
|
||||
|
||||
if (wkeydata != NULL) {
|
||||
hidden_args = fnvlist_alloc();
|
||||
fnvlist_add_uint8_array(hidden_args, "wkeydata", wkeydata,
|
||||
wkeylen);
|
||||
fnvlist_add_nvlist(ioc_args, ZPOOL_HIDDEN_ARGS, hidden_args);
|
||||
}
|
||||
|
||||
if (props != NULL)
|
||||
fnvlist_add_nvlist(ioc_args, "props", props);
|
||||
|
||||
error = lzc_ioctl(ZFS_IOC_CHANGE_KEY, fsname, ioc_args, NULL);
|
||||
nvlist_free(hidden_args);
|
||||
nvlist_free(ioc_args);
|
||||
return (error);
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ KERNEL_C = \
|
||||
dsl_deadlist.c \
|
||||
dsl_deleg.c \
|
||||
dsl_dir.c \
|
||||
dsl_crypt.c \
|
||||
dsl_pool.c \
|
||||
dsl_prop.c \
|
||||
dsl_scan.c \
|
||||
@@ -128,6 +129,7 @@ KERNEL_C = \
|
||||
zio.c \
|
||||
zio_checksum.c \
|
||||
zio_compress.c \
|
||||
zio_crypt.c \
|
||||
zio_inject.c \
|
||||
zle.c \
|
||||
zrlock.c
|
||||
|
||||
Reference in New Issue
Block a user