mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 02:27:36 +03:00
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:
committed by
Brian Behlendorf
parent
fe8a7982ca
commit
d9c460a0b6
+72
-21
@@ -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));
|
||||
|
||||
+43
-27
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user