Permalink
Browse files

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>
  • Loading branch information...
loli10K authored and ahrens committed Feb 8, 2019
1 parent 4417096 commit d8d418ff0cc90776182534bce10b01e9487b63e4
@@ -65,6 +65,7 @@ def enum(*sequential, **named):
ZFS_ERR_NO_CHECKPOINT = 1026
ZFS_ERR_DEVRM_IN_PROGRESS = 1027
ZFS_ERR_VDEV_TOO_BIG = 1028
ZFS_ERR_WRONG_PARENT = 1033


# vim: softtabstop=4 tabstop=4 expandtab shiftwidth=4
@@ -38,21 +38,23 @@
ZFS_ERR_DISCARDING_CHECKPOINT,
ZFS_ERR_NO_CHECKPOINT,
ZFS_ERR_DEVRM_IN_PROGRESS,
ZFS_ERR_VDEV_TOO_BIG
ZFS_ERR_VDEV_TOO_BIG,
ZFS_ERR_WRONG_PARENT
)


def lzc_create_translate_error(ret, name, ds_type, props):
if ret == 0:
return
if ret == errno.EINVAL:
# XXX: should raise lzc_exc.WrongParent if parent is ZVOL
_validate_fs_name(name)
raise lzc_exc.PropertyInvalid(name)
if ret == errno.EEXIST:
raise lzc_exc.FilesystemExists(name)
if ret == errno.ENOENT:
raise lzc_exc.ParentNotFound(name)
if ret == ZFS_ERR_WRONG_PARENT:
raise lzc_exc.WrongParent(_fs_name(name))
raise _generic_exception(ret, name, "Failed to create filesystem")


@@ -444,6 +446,8 @@ def _map(ret, name):
raise lzc_exc.SuspendedPool(_pool_name(snapname))
if ret == errno.EBADE: # ECKSUM
raise lzc_exc.BadStream()
if ret == ZFS_ERR_WRONG_PARENT:
raise lzc_exc.WrongParent(_fs_name(snapname))

raise lzc_exc.StreamIOError(ret)

@@ -596,6 +600,8 @@ def lzc_rename_translate_error(ret, source, target):
raise lzc_exc.FilesystemExists(target)
if ret == errno.ENOENT:
raise lzc_exc.FilesystemNotFound(source)
if ret == ZFS_ERR_WRONG_PARENT:
raise lzc_exc.WrongParent(target)
raise _generic_exception(ret, source, "Failed to rename dataset")


@@ -754,6 +754,8 @@ def lzc_receive(snapname, fd, force=False, raw=False, origin=None, props=None):
supported on this side.
:raises NameInvalid: if the name of either snapshot is invalid.
:raises NameTooLong: if the name of either snapshot is too long.
:raises WrongParent: if the parent dataset of the received destination is
not a filesystem (e.g. ZVOL)
.. note::
The ``origin`` is ignored if the actual stream is an incremental stream
@@ -1621,6 +1623,8 @@ def lzc_rename(source, target):
:raises FilesystemNotFound: if the target's parent does not exist.
:raises FilesystemExists: if the target already exists.
:raises PoolsDiffer: if the source and target belong to different pools.
:raises WrongParent: if the "new" parent dataset is not a filesystem
(e.g. ZVOL)
'''
ret = _lib.lzc_rename(source, target)
errors.lzc_rename_translate_error(ret, source, target)
@@ -25,7 +25,8 @@
ZFS_ERR_DISCARDING_CHECKPOINT,
ZFS_ERR_NO_CHECKPOINT,
ZFS_ERR_DEVRM_IN_PROGRESS,
ZFS_ERR_VDEV_TOO_BIG
ZFS_ERR_VDEV_TOO_BIG,
ZFS_ERR_WRONG_PARENT
)


@@ -140,7 +141,7 @@ def __init__(self, name):


class WrongParent(ZFSError):
errno = errno.EINVAL
errno = ZFS_ERR_WRONG_PARENT
message = "Parent dataset is not a filesystem"

def __init__(self, name):
Oops, something went wrong.

0 comments on commit d8d418f

Please sign in to comment.