public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Stoiko Ivanov <s.ivanov@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH zfsonlinux 1/4] cherry-pick patch for unencrypted send
Date: Wed, 18 Mar 2026 13:40:14 +0100	[thread overview]
Message-ID: <20260318124659.374754-2-s.ivanov@proxmox.com> (raw)
In-Reply-To: <20260318124659.374754-1-s.ivanov@proxmox.com>

The patch [0] is a prerequisite to support ZFS native encryption with
storage replication[1].

[0] https://github.com/openzfs/zfs/pull/18240
[1] https://bugzilla.proxmox.com/show_bug.cgi?id=2350

Signed-off-by: Stoiko Ivanov <s.ivanov@proxmox.com>
---
 ...0015-Add-no-preserve-encryption-flag.patch | 306 ++++++++++++++++++
 debian/patches/series                         |   1 +
 2 files changed, 307 insertions(+)
 create mode 100644 debian/patches/0015-Add-no-preserve-encryption-flag.patch

diff --git a/debian/patches/0015-Add-no-preserve-encryption-flag.patch b/debian/patches/0015-Add-no-preserve-encryption-flag.patch
new file mode 100644
index 000000000..ccb160f3f
--- /dev/null
+++ b/debian/patches/0015-Add-no-preserve-encryption-flag.patch
@@ -0,0 +1,306 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Idefix2020 <idefix2020dev@gmail.com>
+Date: Fri, 6 Mar 2026 00:08:17 +0100
+Subject: [PATCH] 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
+---
+ cmd/zfs/zfs_main.c                            | 10 ++--
+ include/libzfs.h                              |  3 ++
+ lib/libzfs/libzfs_sendrecv.c                  | 46 +++++++++++++------
+ man/man8/zfs-send.8                           | 17 +++++--
+ .../functional/rsend/send_encrypted_props.ksh |  8 ++++
+ 5 files changed, 62 insertions(+), 22 deletions(-)
+
+diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c
+index d39b6fe5f76b4d8cb4169718c753b994870560bb..9b5891e0c86bc5621886ef9e8edc0c784a573802 100644
+--- a/cmd/zfs/zfs_main.c
++++ b/cmd/zfs/zfs_main.c
+@@ -345,10 +345,10 @@ get_usage(zfs_help_t idx)
+ 	case HELP_ROLLBACK:
+ 		return (gettext("\trollback [-rRf] <snapshot>\n"));
+ 	case HELP_SEND:
+-		return (gettext("\tsend [-DLPbcehnpsVvw] "
++		return (gettext("\tsend [-DLPbcehnpsUVvw] "
+ 		    "[-i|-I snapshot]\n"
+ 		    "\t     [-R [-X dataset[,dataset]...]]     <snapshot>\n"
+-		    "\tsend [-DnVvPLecw] [-i snapshot|bookmark] "
++		    "\tsend [-DnVvPLecwU] [-i snapshot|bookmark] "
+ 		    "<filesystem|volume|snapshot>\n"
+ 		    "\tsend [-DnPpVvLec] [-i bookmark|snapshot] "
+ 		    "--redact <bookmark> <snapshot>\n"
+@@ -4784,11 +4784,12 @@ zfs_do_send(int argc, char **argv)
+ 		{"holds",	no_argument,		NULL, 'h'},
+ 		{"saved",	no_argument,		NULL, 'S'},
+ 		{"exclude",	required_argument,	NULL, 'X'},
++		{"no-preserve-encryption",	no_argument,	NULL, 'U'},
+ 		{0, 0, 0, 0}
+ 	};
+ 
+ 	/* 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) {
+ 		switch (c) {
+ 		case 'X':
+@@ -4874,6 +4875,9 @@ zfs_do_send(int argc, char **argv)
+ 		case 'S':
+ 			flags.saved = B_TRUE;
+ 			break;
++		case 'U':
++			flags.no_preserve_encryption = B_TRUE;
++			break;
+ 		case ':':
+ 			/*
+ 			 * If a parameter was not passed, optopt contains the
+diff --git a/include/libzfs.h b/include/libzfs.h
+index 14930fb90622b706d7c45991817746a61e0fdd2d..c67d5f353e99b4273728ccffde1f4db6db77835b 100644
+--- a/include/libzfs.h
++++ b/include/libzfs.h
+@@ -845,6 +845,9 @@ typedef struct sendflags {
+ 
+ 	/* stream represents a partially received dataset */
+ 	boolean_t saved;
++
++	/* allow sending datasets with props, without preserving encryption */
++	boolean_t no_preserve_encryption;
+ } sendflags_t;
+ 
+ typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *);
+diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c
+index 0e5cecc6cca9e78e7eb015000b010bab8fa2e36c..9d693223e1c854e94b7b5a0fcf11b089c077caf5 100644
+--- a/lib/libzfs/libzfs_sendrecv.c
++++ b/lib/libzfs/libzfs_sendrecv.c
+@@ -258,6 +258,7 @@ typedef struct send_data {
+ 	boolean_t seento;
+ 	boolean_t holds;	/* were holds requested with send -h */
+ 	boolean_t props;
++	boolean_t no_preserve_encryption;
+ 
+ 	/*
+ 	 * 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");
+ 
+ 		/*
+-		 * 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.
++		 * Encrypted datasets can only be sent with properties if the
++		 * raw flag or the no-preserve-encryption flag are specified
++		 * because the receive side doesn't currently have a mechanism
++		 * 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,
+ 			    "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);
+ 			rv = -1;
+ 			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,
+     const char *tosnap, boolean_t recursive, boolean_t raw, boolean_t doall,
+     boolean_t replicate, boolean_t skipmissing, boolean_t verbose,
+-    boolean_t backup, boolean_t holds, boolean_t props, nvlist_t **nvlp,
+-    avl_tree_t **avlp)
++    boolean_t backup, boolean_t holds, boolean_t props,
++	boolean_t no_preserve_encryption, nvlist_t **nvlp, avl_tree_t **avlp)
+ {
+ 	zfs_handle_t *zhp;
+ 	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.holds = holds;
+ 	sd.props = props;
++	sd.no_preserve_encryption = no_preserve_encryption;
+ 
+ 	if ((error = send_iterate_fs(zhp, &sd)) != 0) {
+ 		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 dryrun, boolean_t raw, boolean_t replicate, boolean_t skipmissing,
+     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;
+ 	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,
+ 		    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,
+ 			    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->raw, flags->replicate, flags->skipmissing,
+ 		    flags->backup, flags->holds, flags->props, flags->doall,
+-		    &fss, &fsavl);
++		    flags->no_preserve_encryption, &fss, &fsavl);
+ 		zfs_close(tosnap);
+ 		if (err != 0)
+ 			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,
+ 		    flags->verbosity > 0, flags->dryrun, flags->raw,
+ 		    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)
+ 			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. */
+ 	if ((err = gather_nvlist(hdl, top_zfs, NULL, NULL,
+ 	    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);
+ 
+ 	/*
+@@ -3547,7 +3563,7 @@ again:
+ 
+ 	if ((error = gather_nvlist(hdl, tofs, fromsnap, NULL,
+ 	    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);
+ 
+ 	/*
+@@ -5138,7 +5154,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
+ 		*cp = '\0';
+ 		if (gather_nvlist(hdl, destsnap, NULL, NULL, B_FALSE, B_TRUE,
+ 		    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 = '@';
+ 			fs = fsavl_find(local_avl, drrb->drr_toguid, NULL);
+ 			fsavl_destroy(local_avl);
+diff --git a/man/man8/zfs-send.8 b/man/man8/zfs-send.8
+index 6c5f6b94afd5beb8389a31910c8f146f861ecade..07952e8f4366481d91f37e213aeb134362a8ed0d 100644
+--- a/man/man8/zfs-send.8
++++ b/man/man8/zfs-send.8
+@@ -31,7 +31,7 @@
+ .\" Copyright 2019 Joyent, Inc.
+ .\" Copyright (c) 2024, Klara, Inc.
+ .\"
+-.Dd August 29, 2025
++.Dd February 20, 2026
+ .Dt ZFS-SEND 8
+ .Os
+ .
+@@ -41,13 +41,13 @@
+ .Sh SYNOPSIS
+ .Nm zfs
+ .Cm send
+-.Op Fl DLPVbcehnpsvw
++.Op Fl DLPUVbcehnpsvw
+ .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
+ .Ar snapshot
+ .Nm zfs
+ .Cm send
+-.Op Fl DLPVcensvw
++.Op Fl DLPUVcensvw
+ .Op Fl i Ar snapshot Ns | Ns Ar bookmark
+ .Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
+ .Nm zfs
+@@ -75,7 +75,7 @@
+ .It Xo
+ .Nm zfs
+ .Cm send
+-.Op Fl DLPVbcehnpsvw
++.Op Fl DLPUVbcehnpsvw
+ .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
+ .Ar snapshot
+@@ -146,6 +146,8 @@ If the
+ .Fl R
+ flag is used to send encrypted datasets, then
+ .Fl w
++or
++.Fl U
+ must also be specified.
+ .It Fl V , -proctitle
+ 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.
+ Sends of encrypted datasets must use
+ .Fl w
++or
++.Fl U
+ when using this flag.
+ .It Fl s , -skip-missing
+ Allows sending a replication stream even when there are snapshots missing in the
+@@ -303,6 +307,11 @@ belongs
+ and its descendants are skipped.
+ This flag can only be used in conjunction with
+ .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
+ Print verbose information about the stream package generated.
+ This information includes a per-second report of how much data has been sent.
+diff --git a/tests/zfs-tests/tests/functional/rsend/send_encrypted_props.ksh b/tests/zfs-tests/tests/functional/rsend/send_encrypted_props.ksh
+index 1e7ca56a143ef2a12064da81c7d9dc58a126b9d3..0c9006d9be4674240c34291f91fc880a3505e4af 100755
+--- a/tests/zfs-tests/tests/functional/rsend/send_encrypted_props.ksh
++++ b/tests/zfs-tests/tests/functional/rsend/send_encrypted_props.ksh
+@@ -41,6 +41,7 @@
+ #    encryption child
+ # 10. Verify that an unencrypted recursive send can be received as an
+ #     encryption child
++# 11. Verify an encrypted pool can be sent with props only when -U is set
+ #
+ 
+ verify_runnable "both"
+@@ -119,6 +120,13 @@ log_mustnot eval "zfs send -i $esnap $esnap2 |" \
+ 	"zfs recv -o pbkdf2iters=100k $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.
+ log_note "Must be able to receive stream as encryption root"
+ ds=$TESTPOOL/recv
diff --git a/debian/patches/series b/debian/patches/series
index 95bf2d047..bc06a2d8d 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -12,3 +12,4 @@
 0012-Linux-7.0-posix_acl_to_xattr-now-allocates-memory.patch
 0013-Linux-7.0-add-shims-for-the-fs_context-based-mount-A.patch
 0014-Linux-7.0-WIP-compat-META.patch
+0015-Add-no-preserve-encryption-flag.patch
-- 
2.47.3





  reply	other threads:[~2026-03-18 12:47 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-18 12:40 [RFC docs/storage/zfsonlinux 0/4] allow replication/migration with zfs native encryption Stoiko Ivanov
2026-03-18 12:40 ` Stoiko Ivanov [this message]
2026-03-18 12:40 ` [PATCH storage 2/4] fix #2350: zfspool: send without preserving encryption Stoiko Ivanov
2026-03-18 12:40 ` [PATCH storage 3/4] zfspool: export: skip hardcoded warning about no-preserve-encryption flag Stoiko Ivanov
2026-03-18 12:40 ` [PATCH docs 4/4] storage: zfspool: add documention on encryption Stoiko Ivanov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260318124659.374754-2-s.ivanov@proxmox.com \
    --to=s.ivanov@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal