mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 18:40:43 +03:00
ZVOLs should not be allowed to have children
zfs create, receive and rename can bypass this hierarchy rule. Update both userland and kernel module to prevent this issue and use pyzfs unit tests to exercise the ioctls directly. Note: this commit slightly changes zfs_ioc_create() ABI. This allow to differentiate a generic error (EINVAL) from the specific case where we tried to create a dataset below a ZVOL (ZFS_ERR_WRONG_PARENT). Reviewed-by: Paul Dagnelie <pcd@delphix.com> Reviewed-by: Matt Ahrens <mahrens@delphix.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Tom Caputi <tcaputi@datto.com> Signed-off-by: loli10K <ezomori.nozomu@gmail.com>
This commit is contained in:
@@ -3809,11 +3809,6 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
|
||||
"no such parent '%s'"), parent);
|
||||
return (zfs_error(hdl, EZFS_NOENT, errbuf));
|
||||
|
||||
case EINVAL:
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"parent '%s' is not a filesystem"), parent);
|
||||
return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
|
||||
|
||||
case ENOTSUP:
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"pool must be upgraded to set this "
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
* Copyright (c) 2013 Steven Hartland. All rights reserved.
|
||||
* Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
|
||||
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
|
||||
* Copyright (c) 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
|
||||
* Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
@@ -3912,6 +3912,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
* - we are resuming a failed receive.
|
||||
*/
|
||||
if (stream_wantsnewfs) {
|
||||
boolean_t is_volume = drrb->drr_type == DMU_OST_ZVOL;
|
||||
if (!flags->force) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"destination '%s' exists\n"
|
||||
@@ -3928,6 +3929,24 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
err = zfs_error(hdl, EZFS_EXISTS, errbuf);
|
||||
goto out;
|
||||
}
|
||||
if (is_volume && strrchr(name, '/') == NULL) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"destination %s is the root dataset\n"
|
||||
"cannot overwrite with a ZVOL"),
|
||||
name);
|
||||
err = zfs_error(hdl, EZFS_EXISTS, errbuf);
|
||||
goto out;
|
||||
}
|
||||
if (is_volume &&
|
||||
ioctl(hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT,
|
||||
&zc) == 0) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"destination has children (eg. %s)\n"
|
||||
"cannot overwrite with a ZVOL"),
|
||||
zc.zc_name);
|
||||
err = zfs_error(hdl, EZFS_WRONG_PARENT, errbuf);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if ((zhp = zfs_open(hdl, name,
|
||||
@@ -4051,6 +4070,20 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* validate parent */
|
||||
zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
|
||||
if (zhp == NULL) {
|
||||
err = zfs_error(hdl, EZFS_BADRESTORE, errbuf);
|
||||
goto out;
|
||||
}
|
||||
if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"parent '%s' is not a filesystem"), name);
|
||||
err = zfs_error(hdl, EZFS_WRONG_PARENT, errbuf);
|
||||
zfs_close(zhp);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* It is invalid to receive a properties stream that was
|
||||
* unencrypted on the send side as a child of an encrypted
|
||||
@@ -4068,23 +4101,18 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||
zfs_prop_to_name(ZFS_PROP_ENCRYPTION))) {
|
||||
uint64_t crypt;
|
||||
|
||||
zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
|
||||
if (zhp == NULL) {
|
||||
err = zfs_error(hdl, EZFS_BADRESTORE, errbuf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION);
|
||||
zfs_close(zhp);
|
||||
|
||||
if (crypt != ZIO_CRYPT_OFF) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"parent '%s' must not be encrypted to "
|
||||
"receive unenecrypted property"), name);
|
||||
err = zfs_error(hdl, EZFS_BADPROP, errbuf);
|
||||
zfs_close(zhp);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
zfs_close(zhp);
|
||||
|
||||
newfs = B_TRUE;
|
||||
*cp = '/';
|
||||
|
||||
@@ -290,6 +290,8 @@ libzfs_error_description(libzfs_handle_t *hdl)
|
||||
case EZFS_NO_INITIALIZE:
|
||||
return (dgettext(TEXT_DOMAIN, "there is no active "
|
||||
"initialization"));
|
||||
case EZFS_WRONG_PARENT:
|
||||
return (dgettext(TEXT_DOMAIN, "invalid parent dataset"));
|
||||
case EZFS_UNKNOWN:
|
||||
return (dgettext(TEXT_DOMAIN, "unknown error"));
|
||||
default:
|
||||
@@ -471,6 +473,9 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
|
||||
case ZFS_ERR_IOC_ARG_BADTYPE:
|
||||
zfs_verror(hdl, EZFS_IOC_NOTSUPPORTED, fmt, ap);
|
||||
break;
|
||||
case ZFS_ERR_WRONG_PARENT:
|
||||
zfs_verror(hdl, EZFS_WRONG_PARENT, fmt, ap);
|
||||
break;
|
||||
default:
|
||||
zfs_error_aux(hdl, strerror(error));
|
||||
zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
|
||||
|
||||
Reference in New Issue
Block a user