diff options
-rw-r--r-- | lib/libzfs/libzfs_sendrecv.c | 35 | ||||
-rw-r--r-- | module/zfs/dmu_send.c | 10 |
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; |