summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/libzfs/libzfs_sendrecv.c35
-rw-r--r--module/zfs/dmu_send.c10
2 files changed, 33 insertions, 12 deletions
diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c
index 9ac2a9026..c850c8ba6 100644
--- a/lib/libzfs/libzfs_sendrecv.c
+++ b/lib/libzfs/libzfs_sendrecv.c
@@ -3832,6 +3832,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
if (zfs_dataset_exists(hdl, name, ZFS_TYPE_DATASET)) {
zfs_cmd_t zc = {"\0"};
zfs_handle_t *zhp;
+ boolean_t encrypted;
(void) strcpy(zc.zc_name, name);
@@ -3879,24 +3880,36 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
}
/*
- * 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.
+ * Raw sends can not be performed as an incremental on top
+ * of existing unencryppted datasets. 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
+ * 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) {
+ encrypted = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) !=
+ ZIO_CRYPT_OFF;
+ if (!stream_wantsnewfs && !encrypted && raw) {
zfs_close(zhp);
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "zfs receive -F cannot be used to "
- "destroy an encrypted filesystem"));
+ "cannot perform raw receive on top of "
+ "existing unencrypted dataset"));
err = zfs_error(hdl, EZFS_BADRESTORE, errbuf);
goto out;
}
+ if (stream_wantsnewfs && flags->force &&
+ ((raw && !encrypted) || encrypted)) {
+ zfs_close(zhp);
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "zfs receive -F cannot be used to destroy an "
+ "encrypted filesystem or overwrite an "
+ "unencrypted one with an encrypted one"));
+ err = zfs_error(hdl, EZFS_BADRESTORE, errbuf);
+ goto out;
+ }
if (!flags->dryrun && zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&
stream_wantsnewfs) {
diff --git a/module/zfs/dmu_send.c b/module/zfs/dmu_send.c
index 8ca77e95d..bbb319822 100644
--- a/module/zfs/dmu_send.c
+++ b/module/zfs/dmu_send.c
@@ -1522,6 +1522,10 @@ recv_begin_check_existing_impl(dmu_recv_begin_arg_t *drba, dsl_dataset_t *ds,
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;
/* temporary clone name must not exist */
error = zap_lookup(dp->dp_meta_objset,
@@ -1555,6 +1559,10 @@ recv_begin_check_existing_impl(dmu_recv_begin_arg_t *drba, dsl_dataset_t *ds,
dsl_dataset_t *snap;
uint64_t obj = dsl_dataset_phys(ds)->ds_prev_snap_obj;
+ /* Can't perform a raw receive on top of a non-raw receive */
+ if (!encrypted && raw)
+ return (SET_ERROR(EINVAL));
+
/* Find snapshot in this dir that matches fromguid. */
while (obj != 0) {
error = dsl_dataset_hold_obj(dp, obj, FTAG,
@@ -1599,7 +1607,7 @@ recv_begin_check_existing_impl(dmu_recv_begin_arg_t *drba, dsl_dataset_t *ds,
* dsl dir to point to the old encryption key and
* the new one at the same time during the receive.
*/
- if (ds->ds_dir->dd_crypto_obj != 0)
+ if ((!encrypted && raw) || encrypted)
return (SET_ERROR(EINVAL));
drba->drba_snapobj = 0;