mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-04-13 23:21:46 +03:00
Add --no-preserve-encryption flag
* Add an option to send datasets with params or replicate without preserving encryption * Add a test case for the new functionality Reviewed-by: Paul Dagnelie <paul.dagnelie@klarasystems.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Alexander Motin <alexander.motin@TrueNAS.com> Signed-off-by: Chris Jacobs <idefix2020dev@gmail.com> Closes #18240
This commit is contained in:
parent
c329530e6b
commit
5dad9459d5
@ -344,10 +344,10 @@ get_usage(zfs_help_t idx)
|
|||||||
case HELP_ROLLBACK:
|
case HELP_ROLLBACK:
|
||||||
return (gettext("\trollback [-rRf] <snapshot>\n"));
|
return (gettext("\trollback [-rRf] <snapshot>\n"));
|
||||||
case HELP_SEND:
|
case HELP_SEND:
|
||||||
return (gettext("\tsend [-DLPbcehnpsVvw] "
|
return (gettext("\tsend [-DLPbcehnpsUVvw] "
|
||||||
"[-i|-I snapshot]\n"
|
"[-i|-I snapshot]\n"
|
||||||
"\t [-R [-X dataset[,dataset]...]] <snapshot>\n"
|
"\t [-R [-X dataset[,dataset]...]] <snapshot>\n"
|
||||||
"\tsend [-DnVvPLecw] [-i snapshot|bookmark] "
|
"\tsend [-DnVvPLecwU] [-i snapshot|bookmark] "
|
||||||
"<filesystem|volume|snapshot>\n"
|
"<filesystem|volume|snapshot>\n"
|
||||||
"\tsend [-DnPpVvLec] [-i bookmark|snapshot] "
|
"\tsend [-DnPpVvLec] [-i bookmark|snapshot] "
|
||||||
"--redact <bookmark> <snapshot>\n"
|
"--redact <bookmark> <snapshot>\n"
|
||||||
@ -4753,11 +4753,12 @@ zfs_do_send(int argc, char **argv)
|
|||||||
{"holds", no_argument, NULL, 'h'},
|
{"holds", no_argument, NULL, 'h'},
|
||||||
{"saved", no_argument, NULL, 'S'},
|
{"saved", no_argument, NULL, 'S'},
|
||||||
{"exclude", required_argument, NULL, 'X'},
|
{"exclude", required_argument, NULL, 'X'},
|
||||||
|
{"no-preserve-encryption", no_argument, NULL, 'U'},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* check options */
|
/* check options */
|
||||||
while ((c = getopt_long(argc, argv, ":i:I:RsDpVvnPLeht:cwbd:SX:",
|
while ((c = getopt_long(argc, argv, ":i:I:RsDpVvnPLeht:cwbd:SX:U",
|
||||||
long_options, NULL)) != -1) {
|
long_options, NULL)) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'X':
|
case 'X':
|
||||||
@ -4843,6 +4844,9 @@ zfs_do_send(int argc, char **argv)
|
|||||||
case 'S':
|
case 'S':
|
||||||
flags.saved = B_TRUE;
|
flags.saved = B_TRUE;
|
||||||
break;
|
break;
|
||||||
|
case 'U':
|
||||||
|
flags.no_preserve_encryption = B_TRUE;
|
||||||
|
break;
|
||||||
case ':':
|
case ':':
|
||||||
/*
|
/*
|
||||||
* If a parameter was not passed, optopt contains the
|
* If a parameter was not passed, optopt contains the
|
||||||
|
|||||||
@ -844,6 +844,9 @@ typedef struct sendflags {
|
|||||||
|
|
||||||
/* stream represents a partially received dataset */
|
/* stream represents a partially received dataset */
|
||||||
boolean_t saved;
|
boolean_t saved;
|
||||||
|
|
||||||
|
/* allow sending datasets with props, without preserving encryption */
|
||||||
|
boolean_t no_preserve_encryption;
|
||||||
} sendflags_t;
|
} sendflags_t;
|
||||||
|
|
||||||
typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *);
|
typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *);
|
||||||
|
|||||||
@ -258,6 +258,7 @@ typedef struct send_data {
|
|||||||
boolean_t seento;
|
boolean_t seento;
|
||||||
boolean_t holds; /* were holds requested with send -h */
|
boolean_t holds; /* were holds requested with send -h */
|
||||||
boolean_t props;
|
boolean_t props;
|
||||||
|
boolean_t no_preserve_encryption;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The header nvlist is of the following format:
|
* The header nvlist is of the following format:
|
||||||
@ -587,20 +588,32 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg)
|
|||||||
fnvlist_add_boolean(nvfs, "is_encroot");
|
fnvlist_add_boolean(nvfs, "is_encroot");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Encrypted datasets can only be sent with properties if
|
* Encrypted datasets can only be sent with properties if the
|
||||||
* the raw flag is specified because the receive side doesn't
|
* raw flag or the no-preserve-encryption flag are specified
|
||||||
* currently have a mechanism for recursively asking the user
|
* because the receive side doesn't currently have a mechanism
|
||||||
* for new encryption parameters.
|
* for recursively asking the user for new encryption
|
||||||
|
* parameters.
|
||||||
|
* We allow sending the dataset unencrypted only if the user
|
||||||
|
* explicitly sets the no-preserve-encryption flag.
|
||||||
*/
|
*/
|
||||||
if (!sd->raw) {
|
if (!sd->raw && !sd->no_preserve_encryption) {
|
||||||
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
|
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
|
||||||
"cannot send %s@%s: encrypted dataset %s may not "
|
"cannot send %s@%s: encrypted dataset %s may not "
|
||||||
"be sent with properties without the raw flag\n"),
|
"be sent with properties without the raw flag or "
|
||||||
|
"no-preserve-encryption flag\n"),
|
||||||
sd->fsname, sd->tosnap, zhp->zfs_name);
|
sd->fsname, sd->tosnap, zhp->zfs_name);
|
||||||
rv = -1;
|
rv = -1;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If no-preserve-encryption flag is set, warn the user again */
|
||||||
|
if (!sd->raw && sd->no_preserve_encryption) {
|
||||||
|
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
|
||||||
|
"WARNING: no-preserve-encryption flag set, sending "
|
||||||
|
"dataset %s without encryption\n"),
|
||||||
|
zhp->zfs_name);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -683,8 +696,8 @@ static int
|
|||||||
gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
|
gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
|
||||||
const char *tosnap, boolean_t recursive, boolean_t raw, boolean_t doall,
|
const char *tosnap, boolean_t recursive, boolean_t raw, boolean_t doall,
|
||||||
boolean_t replicate, boolean_t skipmissing, boolean_t verbose,
|
boolean_t replicate, boolean_t skipmissing, boolean_t verbose,
|
||||||
boolean_t backup, boolean_t holds, boolean_t props, nvlist_t **nvlp,
|
boolean_t backup, boolean_t holds, boolean_t props,
|
||||||
avl_tree_t **avlp)
|
boolean_t no_preserve_encryption, nvlist_t **nvlp, avl_tree_t **avlp)
|
||||||
{
|
{
|
||||||
zfs_handle_t *zhp;
|
zfs_handle_t *zhp;
|
||||||
send_data_t sd = { 0 };
|
send_data_t sd = { 0 };
|
||||||
@ -707,6 +720,7 @@ gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
|
|||||||
sd.backup = backup;
|
sd.backup = backup;
|
||||||
sd.holds = holds;
|
sd.holds = holds;
|
||||||
sd.props = props;
|
sd.props = props;
|
||||||
|
sd.no_preserve_encryption = no_preserve_encryption;
|
||||||
|
|
||||||
if ((error = send_iterate_fs(zhp, &sd)) != 0) {
|
if ((error = send_iterate_fs(zhp, &sd)) != 0) {
|
||||||
fnvlist_free(sd.fss);
|
fnvlist_free(sd.fss);
|
||||||
@ -2199,7 +2213,7 @@ send_prelim_records(zfs_handle_t *zhp, const char *from, int fd,
|
|||||||
boolean_t gather_props, boolean_t recursive, boolean_t verbose,
|
boolean_t gather_props, boolean_t recursive, boolean_t verbose,
|
||||||
boolean_t dryrun, boolean_t raw, boolean_t replicate, boolean_t skipmissing,
|
boolean_t dryrun, boolean_t raw, boolean_t replicate, boolean_t skipmissing,
|
||||||
boolean_t backup, boolean_t holds, boolean_t props, boolean_t doall,
|
boolean_t backup, boolean_t holds, boolean_t props, boolean_t doall,
|
||||||
nvlist_t **fssp, avl_tree_t **fsavlp)
|
boolean_t no_preserve_encryption, nvlist_t **fssp, avl_tree_t **fsavlp)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
char *packbuf = NULL;
|
char *packbuf = NULL;
|
||||||
@ -2245,7 +2259,8 @@ send_prelim_records(zfs_handle_t *zhp, const char *from, int fd,
|
|||||||
|
|
||||||
if (gather_nvlist(zhp->zfs_hdl, tofs,
|
if (gather_nvlist(zhp->zfs_hdl, tofs,
|
||||||
from, tosnap, recursive, raw, doall, replicate, skipmissing,
|
from, tosnap, recursive, raw, doall, replicate, skipmissing,
|
||||||
verbose, backup, holds, props, &fss, fsavlp) != 0) {
|
verbose, backup, holds, props, no_preserve_encryption,
|
||||||
|
&fss, fsavlp) != 0) {
|
||||||
return (zfs_error(zhp->zfs_hdl, EZFS_BADBACKUP,
|
return (zfs_error(zhp->zfs_hdl, EZFS_BADBACKUP,
|
||||||
errbuf));
|
errbuf));
|
||||||
}
|
}
|
||||||
@ -2392,7 +2407,7 @@ zfs_send_cb_impl(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
|
|||||||
flags->replicate, flags->verbosity > 0, flags->dryrun,
|
flags->replicate, flags->verbosity > 0, flags->dryrun,
|
||||||
flags->raw, flags->replicate, flags->skipmissing,
|
flags->raw, flags->replicate, flags->skipmissing,
|
||||||
flags->backup, flags->holds, flags->props, flags->doall,
|
flags->backup, flags->holds, flags->props, flags->doall,
|
||||||
&fss, &fsavl);
|
flags->no_preserve_encryption, &fss, &fsavl);
|
||||||
zfs_close(tosnap);
|
zfs_close(tosnap);
|
||||||
if (err != 0)
|
if (err != 0)
|
||||||
goto err_out;
|
goto err_out;
|
||||||
@ -2735,7 +2750,8 @@ zfs_send_one_cb_impl(zfs_handle_t *zhp, const char *from, int fd,
|
|||||||
err = send_prelim_records(zhp, NULL, fd, B_TRUE, B_FALSE,
|
err = send_prelim_records(zhp, NULL, fd, B_TRUE, B_FALSE,
|
||||||
flags->verbosity > 0, flags->dryrun, flags->raw,
|
flags->verbosity > 0, flags->dryrun, flags->raw,
|
||||||
flags->replicate, B_FALSE, flags->backup, flags->holds,
|
flags->replicate, B_FALSE, flags->backup, flags->holds,
|
||||||
flags->props, flags->doall, NULL, NULL);
|
flags->props, flags->doall, flags->no_preserve_encryption,
|
||||||
|
NULL, NULL);
|
||||||
if (err != 0)
|
if (err != 0)
|
||||||
return (err);
|
return (err);
|
||||||
}
|
}
|
||||||
@ -3392,7 +3408,7 @@ recv_fix_encryption_hierarchy(libzfs_handle_t *hdl, const char *top_zfs,
|
|||||||
/* Using top_zfs, gather the nvlists for all local filesystems. */
|
/* Using top_zfs, gather the nvlists for all local filesystems. */
|
||||||
if ((err = gather_nvlist(hdl, top_zfs, NULL, NULL,
|
if ((err = gather_nvlist(hdl, top_zfs, NULL, NULL,
|
||||||
recursive, B_TRUE, B_FALSE, recursive, B_FALSE, B_FALSE, B_FALSE,
|
recursive, B_TRUE, B_FALSE, recursive, B_FALSE, B_FALSE, B_FALSE,
|
||||||
B_FALSE, B_TRUE, &local_nv, &local_avl)) != 0)
|
B_FALSE, B_TRUE, B_FALSE, &local_nv, &local_avl)) != 0)
|
||||||
return (err);
|
return (err);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3547,7 +3563,7 @@ again:
|
|||||||
|
|
||||||
if ((error = gather_nvlist(hdl, tofs, fromsnap, NULL,
|
if ((error = gather_nvlist(hdl, tofs, fromsnap, NULL,
|
||||||
recursive, B_TRUE, B_FALSE, recursive, B_FALSE, B_FALSE, B_FALSE,
|
recursive, B_TRUE, B_FALSE, recursive, B_FALSE, B_FALSE, B_FALSE,
|
||||||
B_FALSE, B_TRUE, &local_nv, &local_avl)) != 0)
|
B_FALSE, B_TRUE, B_FALSE, &local_nv, &local_avl)) != 0)
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -5138,7 +5154,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
|||||||
*cp = '\0';
|
*cp = '\0';
|
||||||
if (gather_nvlist(hdl, destsnap, NULL, NULL, B_FALSE, B_TRUE,
|
if (gather_nvlist(hdl, destsnap, NULL, NULL, B_FALSE, B_TRUE,
|
||||||
B_FALSE, B_FALSE, B_FALSE, B_FALSE, B_FALSE, B_FALSE,
|
B_FALSE, B_FALSE, B_FALSE, B_FALSE, B_FALSE, B_FALSE,
|
||||||
B_TRUE, &local_nv, &local_avl) == 0) {
|
B_TRUE, B_FALSE, &local_nv, &local_avl) == 0) {
|
||||||
*cp = '@';
|
*cp = '@';
|
||||||
fs = fsavl_find(local_avl, drrb->drr_toguid, NULL);
|
fs = fsavl_find(local_avl, drrb->drr_toguid, NULL);
|
||||||
fsavl_destroy(local_avl);
|
fsavl_destroy(local_avl);
|
||||||
|
|||||||
@ -31,7 +31,7 @@
|
|||||||
.\" Copyright 2019 Joyent, Inc.
|
.\" Copyright 2019 Joyent, Inc.
|
||||||
.\" Copyright (c) 2024, Klara, Inc.
|
.\" Copyright (c) 2024, Klara, Inc.
|
||||||
.\"
|
.\"
|
||||||
.Dd August 29, 2025
|
.Dd February 20, 2026
|
||||||
.Dt ZFS-SEND 8
|
.Dt ZFS-SEND 8
|
||||||
.Os
|
.Os
|
||||||
.
|
.
|
||||||
@ -41,13 +41,13 @@
|
|||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm zfs
|
.Nm zfs
|
||||||
.Cm send
|
.Cm send
|
||||||
.Op Fl DLPVbcehnpsvw
|
.Op Fl DLPUVbcehnpsvw
|
||||||
.Op Fl R Op Fl X Ar dataset Ns Oo , Ns Ar dataset Oc Ns …
|
.Op Fl R Op Fl X Ar dataset Ns Oo , Ns Ar dataset Oc Ns …
|
||||||
.Op Oo Fl I Ns | Ns Fl i Oc Ar snapshot
|
.Op Oo Fl I Ns | Ns Fl i Oc Ar snapshot
|
||||||
.Ar snapshot
|
.Ar snapshot
|
||||||
.Nm zfs
|
.Nm zfs
|
||||||
.Cm send
|
.Cm send
|
||||||
.Op Fl DLPVcensvw
|
.Op Fl DLPUVcensvw
|
||||||
.Op Fl i Ar snapshot Ns | Ns Ar bookmark
|
.Op Fl i Ar snapshot Ns | Ns Ar bookmark
|
||||||
.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
|
.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
|
||||||
.Nm zfs
|
.Nm zfs
|
||||||
@ -75,7 +75,7 @@
|
|||||||
.It Xo
|
.It Xo
|
||||||
.Nm zfs
|
.Nm zfs
|
||||||
.Cm send
|
.Cm send
|
||||||
.Op Fl DLPVbcehnpsvw
|
.Op Fl DLPUVbcehnpsvw
|
||||||
.Op Fl R Op Fl X Ar dataset Ns Oo , Ns Ar dataset Oc Ns …
|
.Op Fl R Op Fl X Ar dataset Ns Oo , Ns Ar dataset Oc Ns …
|
||||||
.Op Oo Fl I Ns | Ns Fl i Oc Ar snapshot
|
.Op Oo Fl I Ns | Ns Fl i Oc Ar snapshot
|
||||||
.Ar snapshot
|
.Ar snapshot
|
||||||
@ -146,6 +146,8 @@ If the
|
|||||||
.Fl R
|
.Fl R
|
||||||
flag is used to send encrypted datasets, then
|
flag is used to send encrypted datasets, then
|
||||||
.Fl w
|
.Fl w
|
||||||
|
or
|
||||||
|
.Fl U
|
||||||
must also be specified.
|
must also be specified.
|
||||||
.It Fl V , -proctitle
|
.It Fl V , -proctitle
|
||||||
Set the process title to a per-second report of how much data has been sent.
|
Set the process title to a per-second report of how much data has been sent.
|
||||||
@ -293,6 +295,8 @@ is specified.
|
|||||||
The receiving system must also support this feature.
|
The receiving system must also support this feature.
|
||||||
Sends of encrypted datasets must use
|
Sends of encrypted datasets must use
|
||||||
.Fl w
|
.Fl w
|
||||||
|
or
|
||||||
|
.Fl U
|
||||||
when using this flag.
|
when using this flag.
|
||||||
.It Fl s , -skip-missing
|
.It Fl s , -skip-missing
|
||||||
Allows sending a replication stream even when there are snapshots missing in the
|
Allows sending a replication stream even when there are snapshots missing in the
|
||||||
@ -303,6 +307,11 @@ belongs
|
|||||||
and its descendants are skipped.
|
and its descendants are skipped.
|
||||||
This flag can only be used in conjunction with
|
This flag can only be used in conjunction with
|
||||||
.Fl R .
|
.Fl R .
|
||||||
|
.It Fl U , -no-preserve-encryption
|
||||||
|
Allow sending an encrypted dataset with properties, but without keeping
|
||||||
|
encryption.
|
||||||
|
When this flag is specified, encrypted datasets that would otherwise be blocked
|
||||||
|
from sending are sent as unencrypted data.
|
||||||
.It Fl v , -verbose
|
.It Fl v , -verbose
|
||||||
Print verbose information about the stream package generated.
|
Print verbose information about the stream package generated.
|
||||||
This information includes a per-second report of how much data has been sent.
|
This information includes a per-second report of how much data has been sent.
|
||||||
|
|||||||
@ -41,6 +41,7 @@
|
|||||||
# encryption child
|
# encryption child
|
||||||
# 10. Verify that an unencrypted recursive send can be received as an
|
# 10. Verify that an unencrypted recursive send can be received as an
|
||||||
# encryption child
|
# encryption child
|
||||||
|
# 11. Verify an encrypted pool can be sent with props only when -U is set
|
||||||
#
|
#
|
||||||
|
|
||||||
verify_runnable "both"
|
verify_runnable "both"
|
||||||
@ -119,6 +120,13 @@ log_mustnot eval "zfs send -i $esnap $esnap2 |" \
|
|||||||
"zfs recv -o pbkdf2iters=100k $TESTPOOL/recv"
|
"zfs recv -o pbkdf2iters=100k $TESTPOOL/recv"
|
||||||
log_must zfs destroy -r $TESTPOOL/recv
|
log_must zfs destroy -r $TESTPOOL/recv
|
||||||
|
|
||||||
|
# The user has to explicitly allow sending a dataset unecrypted when sending
|
||||||
|
# an encrypted dataset with properties
|
||||||
|
log_note "Must not be able to send an encrypted dataset with props unless the -U flag is set"
|
||||||
|
log_mustnot eval "zfs send -p $esnap | zfs recv $TESTPOOL/recv"
|
||||||
|
log_must eval "zfs send -p -U $esnap | zfs recv $TESTPOOL/recv"
|
||||||
|
log_must zfs destroy -r $TESTPOOL/recv
|
||||||
|
|
||||||
# Test that we can receive a simple stream as an encryption root.
|
# Test that we can receive a simple stream as an encryption root.
|
||||||
log_note "Must be able to receive stream as encryption root"
|
log_note "Must be able to receive stream as encryption root"
|
||||||
ds=$TESTPOOL/recv
|
ds=$TESTPOOL/recv
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user