]> git.proxmox.com Git - mirror_zfs.git/blobdiff - contrib/pyzfs/libzfs_core/_libzfs_core.py
Remove code for zfs remap
[mirror_zfs.git] / contrib / pyzfs / libzfs_core / _libzfs_core.py
index 00824f5f6b946f7d79916f8459a3693306551ce8..5a4843943940f885722b5c79dfb3152582b6be69 100644 (file)
@@ -1,4 +1,18 @@
-# Copyright 2015 ClusterHQ. See LICENSE file for details.
+#
+# Copyright 2015 ClusterHQ
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
 
 """
 Python wrappers for libzfs_core interfaces.
@@ -12,6 +26,7 @@ increased convenience.  Output parameters are not used and return values
 are directly returned.  Error conditions are signalled by exceptions
 rather than by integer error codes.
 """
+from __future__ import absolute_import, division, print_function
 
 import errno
 import functools
@@ -22,31 +37,85 @@ import threading
 from . import exceptions
 from . import _error_translation as errors
 from .bindings import libzfs_core
-from ._constants import MAXNAMELEN
-from .ctypes import int32_t
+from ._constants import (  # noqa: F401
+    MAXNAMELEN,
+    ZCP_DEFAULT_INSTRLIMIT,
+    ZCP_DEFAULT_MEMLIMIT,
+    WRAPPING_KEY_LEN,
+    zfs_key_location,
+    zfs_keyformat,
+    zio_encrypt
+)
+from .ctypes import (
+    int32_t,
+    uint64_t
+)
 from ._nvlist import nvlist_in, nvlist_out
 
 
-def lzc_create(name, ds_type='zfs', props=None):
+def _uncommitted(depends_on=None):
+    '''
+    Mark an API function as being an uncommitted extension that might not be
+    available.
+
+    :param function depends_on: the function that would be checked instead of
+        a decorated function. For example, if the decorated function uses
+        another uncommitted function.
+
+    This decorator transforms a decorated function to raise
+    :exc:`NotImplementedError` if the C libzfs_core library does not provide
+    a function with the same name as the decorated function.
+
+    The optional `depends_on` parameter can be provided if the decorated
+    function does not directly call the C function but instead calls another
+    Python function that follows the typical convention.
+    One example is :func:`lzc_list_snaps` that calls :func:`lzc_list` that
+    calls ``lzc_list`` in libzfs_core.
+
+    This decorator is implemented using :func:`is_supported`.
+    '''
+    def _uncommitted_decorator(func, depends_on=depends_on):
+        @functools.wraps(func)
+        def _f(*args, **kwargs):
+            if not is_supported(_f):
+                raise NotImplementedError(func.__name__)
+            return func(*args, **kwargs)
+        if depends_on is not None:
+            _f._check_func = depends_on
+        return _f
+    return _uncommitted_decorator
+
+
+def lzc_create(name, ds_type='zfs', props=None, key=None):
     '''
     Create a ZFS filesystem or a ZFS volume ("zvol").
 
     :param bytes name: a name of the dataset to be created.
-    :param str ds_type: the type of the dataset to be create, currently supported
-                        types are "zfs" (the default) for a filesystem
-                        and "zvol" for a volume.
-    :param props: a `dict` of ZFS dataset property name-value pairs (empty by default).
+    :param str ds_type: the type of the dataset to be created,
+        currently supported types are "zfs" (the default) for a filesystem and
+        "zvol" for a volume.
+    :param props: a `dict` of ZFS dataset property name-value pairs
+        (empty by default).
     :type props: dict of bytes:Any
+    :param key: dataset encryption key data (empty by default).
+    :type key: bytes
 
     :raises FilesystemExists: if a dataset with the given name already exists.
-    :raises ParentNotFound: if a parent dataset of the requested dataset does not exist.
-    :raises PropertyInvalid: if one or more of the specified properties is invalid
-                             or has an invalid type or value.
+    :raises ParentNotFound: if a parent dataset of the requested dataset does
+        not exist.
+    :raises PropertyInvalid: if one or more of the specified properties is
+        invalid or has an invalid type or value.
     :raises NameInvalid: if the name is not a valid dataset name.
     :raises NameTooLong: if the name is too long.
+    :raises WrongParent: if the parent dataset of the requested dataset is not
+        a filesystem (e.g. ZVOL)
     '''
     if props is None:
         props = {}
+    if key is None:
+        key = b""
+    else:
+        key = bytes(key)
     if ds_type == 'zfs':
         ds_type = _lib.DMU_OST_ZFS
     elif ds_type == 'zvol':
@@ -54,7 +123,7 @@ def lzc_create(name, ds_type='zfs', props=None):
     else:
         raise exceptions.DatasetTypeInvalid(ds_type)
     nvlist = nvlist_in(props)
-    ret = _lib.lzc_create(name, ds_type, nvlist)
+    ret = _lib.lzc_create(name, ds_type, nvlist, key, len(key))
     errors.lzc_create_translate_error(ret, name, ds_type, props)
 
 
@@ -64,14 +133,15 @@ def lzc_clone(name, origin, props=None):
 
     :param bytes name: a name of the dataset to be created.
     :param bytes origin: a name of the origin snapshot.
-    :param props: a `dict` of ZFS dataset property name-value pairs (empty by default).
+    :param props: a `dict` of ZFS dataset property name-value pairs
+        (empty by default).
     :type props: dict of bytes:Any
 
     :raises FilesystemExists: if a dataset with the given name already exists.
-    :raises DatasetNotFound: if either a parent dataset of the requested dataset
-                             or the origin snapshot does not exist.
-    :raises PropertyInvalid: if one or more of the specified properties is invalid
-                             or has an invalid type or value.
+    :raises DatasetNotFound: if either a parent dataset of the requested
+        dataset or the origin snapshot does not exist.
+    :raises PropertyInvalid: if one or more of the specified properties is
+        invalid or has an invalid type or value.
     :raises FilesystemNameInvalid: if the name is not a valid dataset name.
     :raises SnapshotNameInvalid: if the origin is not a valid snapshot name.
     :raises NameTooLong: if the name or the origin name is too long.
@@ -79,11 +149,11 @@ def lzc_clone(name, origin, props=None):
 
     .. note::
         Because of a deficiency of the underlying C interface
-        :exc:`.DatasetNotFound` can mean that either a parent filesystem of the target
-        or the origin snapshot does not exist.
+        :exc:`.DatasetNotFound` can mean that either a parent filesystem of
+        the target or the origin snapshot does not exist.
         It is currently impossible to distinguish between the cases.
-        :func:`lzc_hold` can be used to check that the snapshot exists and ensure that
-        it is not destroyed before cloning.
+        :func:`lzc_hold` can be used to check that the snapshot exists and
+        ensure that it is not destroyed before cloning.
     '''
     if props is None:
         props = {}
@@ -115,6 +185,7 @@ def lzc_rollback(name):
     errors.lzc_rollback_translate_error(ret, name)
     return _ffi.string(snapnamep)
 
+
 def lzc_rollback_to(name, snap):
     '''
     Roll back this filesystem or volume to the specified snapshot, if possible.
@@ -131,6 +202,7 @@ def lzc_rollback_to(name, snap):
     ret = _lib.lzc_rollback_to(name, snap)
     errors.lzc_rollback_to_translate_error(ret, name, snap)
 
+
 def lzc_snapshot(snaps, props=None):
     '''
     Create snapshots.
@@ -145,7 +217,8 @@ def lzc_snapshot(snaps, props=None):
 
     :param snaps: a list of names of snapshots to be created.
     :type snaps: list of bytes
-    :param props: a `dict` of ZFS dataset property name-value pairs (empty by default).
+    :param props: a `dict` of ZFS dataset property name-value pairs
+        (empty by default).
     :type props: dict of bytes:bytes
 
     :raises SnapshotFailure: if one or more snapshots could not be created.
@@ -163,7 +236,8 @@ def lzc_snapshot(snaps, props=None):
 
         This has the following implications:
 
-        * if multiple error conditions are encountered only one of them is reported
+        * if multiple error conditions are encountered only one of them is
+          reported
 
         * unless only one snapshot is requested then it is impossible to tell
           how many snapshots are problematic and what they are
@@ -173,9 +247,9 @@ def lzc_snapshot(snaps, props=None):
 
         * :exc:`.NameTooLong` can behave either in the same way as
           :exc:`.SnapshotExists` or as all other exceptions.
-          The former is the case where the full snapshot name exceeds the maximum
-          allowed length but the short snapshot name (after '@') is within
-          the limit.
+          The former is the case where the full snapshot name exceeds the
+          maximum allowed length but the short snapshot name (after '@') is
+          within the limit.
           The latter is the case when the short name alone exceeds the maximum
           allowed length.
     '''
@@ -214,19 +288,22 @@ def lzc_destroy_snaps(snaps, defer):
     :param snaps: a list of names of snapshots to be destroyed.
     :type snaps: list of bytes
     :param bool defer: whether to mark busy snapshots for deferred destruction
-                       rather than immediately failing.
+        rather than immediately failing.
 
-    :raises SnapshotDestructionFailure: if one or more snapshots could not be created.
+    :raises SnapshotDestructionFailure: if one or more snapshots could not be
+        created.
 
     .. note::
-        :exc:`.SnapshotDestructionFailure` is a compound exception that provides at least
-        one detailed error object in :attr:`SnapshotDestructionFailure.errors` `list`.
+        :exc:`.SnapshotDestructionFailure` is a compound exception that
+        provides at least one detailed error object in
+        :attr:`SnapshotDestructionFailure.errors` `list`.
 
         Typical error is :exc:`SnapshotIsCloned` if `defer` is `False`.
-        The snapshot names are validated quite loosely and invalid names are typically
-        ignored as nonexisiting snapshots.
+        The snapshot names are validated quite loosely and invalid names are
+        typically ignored as nonexisiting snapshots.
 
-        A snapshot name referring to a filesystem that doesn't exist is ignored.
+        A snapshot name referring to a filesystem that doesn't exist is
+        ignored.
         However, non-existent pool name causes :exc:`PoolNotFound`.
     '''
     snaps_dict = {name: None for name in snaps}
@@ -241,14 +318,16 @@ def lzc_bookmark(bookmarks):
     '''
     Create bookmarks.
 
-    :param bookmarks: a dict that maps names of wanted bookmarks to names of existing snapshots.
+    :param bookmarks: a dict that maps names of wanted bookmarks to names of
+        existing snapshots.
     :type bookmarks: dict of bytes to bytes
+    :raises BookmarkFailure: if any of the bookmarks can not be created for any
+        reason.
 
-    :raises BookmarkFailure: if any of the bookmarks can not be created for any reason.
-
-    The bookmarks `dict` maps from name of the bookmark (e.g. :file:`{pool}/{fs}#{bmark}`) to
-    the name of the snapshot (e.g. :file:`{pool}/{fs}@{snap}`).  All the bookmarks and
-    snapshots must be in the same pool.
+    The bookmarks `dict` maps from name of the bookmark
+    (e.g. :file:`{pool}/{fs}#{bmark}`) to the name of the snapshot
+    (e.g. :file:`{pool}/{fs}@{snap}`).  All the bookmarks and snapshots must
+    be in the same pool.
     '''
     errlist = {}
     nvlist = nvlist_in(bookmarks)
@@ -262,7 +341,8 @@ def lzc_get_bookmarks(fsname, props=None):
     Retrieve a listing of bookmarks for the given file system.
 
     :param bytes fsname: a name of the filesystem.
-    :param props: a `list` of properties that will be returned for each bookmark.
+    :param props: a `list` of properties that will be returned for each
+        bookmark.
     :type props: list of bytes
     :return: a `dict` that maps the bookmarks' short names to their properties.
     :rtype: dict of bytes:dict
@@ -298,11 +378,12 @@ def lzc_destroy_bookmarks(bookmarks):
     '''
     Destroy bookmarks.
 
-    :param bookmarks: a list of the bookmarks to be destroyed.
-                      The bookmarks are specified as :file:`{fs}#{bmark}`.
+    :param bookmarks: a list of the bookmarks to be destroyed. The bookmarks
+        are specified as :file:`{fs}#{bmark}`.
     :type bookmarks: list of bytes
 
-    :raises BookmarkDestructionFailure: if any of the bookmarks may not be destroyed.
+    :raises BookmarkDestructionFailure: if any of the bookmarks may not be
+        destroyed.
 
     The bookmarks must all be in the same pool.
     Bookmarks that do not exist will be silently ignored.
@@ -323,8 +404,9 @@ def lzc_destroy_bookmarks(bookmarks):
 
 def lzc_snaprange_space(firstsnap, lastsnap):
     '''
-    Calculate a size of data referenced by snapshots in the inclusive range between
-    the ``firstsnap`` and the ``lastsnap`` and not shared with any other datasets.
+    Calculate a size of data referenced by snapshots in the inclusive range
+    between the ``firstsnap`` and the ``lastsnap`` and not shared with any
+    other datasets.
 
     :param bytes firstsnap: the name of the first snapshot in the range.
     :param bytes lastsnap: the name of the last snapshot in the range.
@@ -334,18 +416,21 @@ def lzc_snaprange_space(firstsnap, lastsnap):
     :raises SnapshotNotFound: if either of the snapshots does not exist.
     :raises NameInvalid: if the name of either snapshot is invalid.
     :raises NameTooLong: if the name of either snapshot is too long.
-    :raises SnapshotMismatch: if ``fromsnap`` is not an ancestor snapshot of ``snapname``.
+    :raises SnapshotMismatch: if ``fromsnap`` is not an ancestor snapshot of
+        ``snapname``.
     :raises PoolsDiffer: if the snapshots belong to different pools.
 
     ``lzc_snaprange_space`` calculates total size of blocks that exist
-    because they are referenced only by one or more snapshots in the given range
-    but no other dataset.
-    In other words, this is the set of blocks that were born after the snap before
-    firstsnap, and died before the snap after the last snap.
-    Yet another interpretation is that the result of ``lzc_snaprange_space`` is the size
-    of the space that would be freed if the snapshots in the range are destroyed.
-
-    If the same snapshot is given as both the ``firstsnap`` and the ``lastsnap``.
+    because they are referenced only by one or more snapshots in the given
+    range but no other dataset.
+    In other words, this is the set of blocks that were born after the snap
+    before firstsnap, and died before the snap after the last snap.
+    Yet another interpretation is that the result of ``lzc_snaprange_space``
+    is the size of the space that would be freed if the snapshots in the range
+    are destroyed.
+
+    If the same snapshot is given as both the ``firstsnap`` and the
+    ``lastsnap``.
     In that case ``lzc_snaprange_space`` calculates space used by the snapshot.
     '''
     valp = _ffi.new('uint64_t *')
@@ -357,19 +442,23 @@ def lzc_snaprange_space(firstsnap, lastsnap):
 def lzc_hold(holds, fd=None):
     '''
     Create *user holds* on snapshots.  If there is a hold on a snapshot,
-    the snapshot can not be destroyed.  (However, it can be marked for deletion
-    by :func:`lzc_destroy_snaps` ( ``defer`` = `True` ).)
+    the snapshot can not be destroyed.  (However, it can be marked for
+    deletion by :func:`lzc_destroy_snaps` ( ``defer`` = `True` ).)
 
-    :param holds: the dictionary of names of the snapshots to hold mapped to the hold names.
+    :param holds: the dictionary of names of the snapshots to hold mapped to
+        the hold names.
     :type holds: dict of bytes : bytes
     :type fd: int or None
-    :param fd: if not None then it must be the result of :func:`os.open` called as ``os.open("/dev/zfs", O_EXCL)``.
+    :param fd: if not None then it must be the result of :func:`os.open`
+        called as ``os.open("/dev/zfs", O_EXCL)``.
     :type fd: int or None
     :return: a list of the snapshots that do not exist.
     :rtype: list of bytes
 
-    :raises HoldFailure: if a hold was impossible on one or more of the snapshots.
-    :raises BadHoldCleanupFD: if ``fd`` is not a valid file descriptor associated with :file:`/dev/zfs`.
+    :raises HoldFailure: if a hold was impossible on one or more of the
+        snapshots.
+    :raises BadHoldCleanupFD: if ``fd`` is not a valid file descriptor
+        associated with :file:`/dev/zfs`.
 
     The snapshots must all be in the same pool.
 
@@ -380,11 +469,13 @@ def lzc_hold(holds, fd=None):
 
     Holds for snapshots which don't exist will be skipped and have an entry
     added to the return value, but will not cause an overall failure.
-    No exceptions is raised if all holds, for snapshots that existed, were succesfully created.
-    Otherwise :exc:`.HoldFailure` exception is raised and no holds will be created.
-    :attr:`.HoldFailure.errors` may contain a single element for an error that is not
-    specific to any hold / snapshot, or it may contain one or more elements
-    detailing specific error per each affected hold.
+    No exceptions is raised if all holds, for snapshots that existed, were
+    succesfully created.
+    Otherwise :exc:`.HoldFailure` exception is raised and no holds will be
+    created.
+    :attr:`.HoldFailure.errors` may contain a single element for an error that
+    is not specific to any hold / snapshot, or it may contain one or more
+    elements detailing specific error per each affected hold.
     '''
     errlist = {}
     if fd is None:
@@ -395,8 +486,8 @@ def lzc_hold(holds, fd=None):
     errors.lzc_hold_translate_errors(ret, errlist, holds, fd)
     # If there is no error (no exception raised by _handleErrList), but errlist
     # is not empty, then it contains missing snapshots.
-    assert all(x == errno.ENOENT for x in errlist.itervalues())
-    return errlist.keys()
+    assert all(errlist[x] == errno.ENOENT for x in errlist)
+    return list(errlist.keys())
 
 
 def lzc_release(holds):
@@ -411,15 +502,16 @@ def lzc_release(holds):
     The snapshots must all be in the same pool.
 
     :param holds: a ``dict`` where keys are snapshot names and values are
-                  lists of hold tags to remove.
+        lists of hold tags to remove.
     :type holds: dict of bytes : list of bytes
-    :return: a list of any snapshots that do not exist and of any tags that do not
-             exist for existing snapshots.
-             Such tags are qualified with a corresponding snapshot name
-             using the following format :file:`{pool}/{fs}@{snap}#{tag}`
+    :return: a list of any snapshots that do not exist and of any tags that do
+        not exist for existing snapshots.
+        Such tags are qualified with a corresponding snapshot name using the
+        following format :file:`{pool}/{fs}@{snap}#{tag}`
     :rtype: list of bytes
 
-    :raises HoldReleaseFailure: if one or more existing holds could not be released.
+    :raises HoldReleaseFailure: if one or more existing holds could not be
+        released.
 
     Holds which failed to release because they didn't exist will have an entry
     added to errlist, but will not cause an overall failure.
@@ -430,7 +522,8 @@ def lzc_release(holds):
     '''
     errlist = {}
     holds_dict = {}
-    for snap, hold_list in holds.iteritems():
+    for snap in holds:
+        hold_list = holds[snap]
         if not isinstance(hold_list, list):
             raise TypeError('holds must be in a list')
         holds_dict[snap] = {hold: None for hold in hold_list}
@@ -440,8 +533,8 @@ def lzc_release(holds):
     errors.lzc_release_translate_errors(ret, errlist, holds)
     # If there is no error (no exception raised by _handleErrList), but errlist
     # is not empty, then it contains missing snapshots and tags.
-    assert all(x == errno.ENOENT for x in errlist.itervalues())
-    return errlist.keys()
+    assert all(errlist[x] == errno.ENOENT for x in errlist)
+    return list(errlist.keys())
 
 
 def lzc_get_holds(snapname):
@@ -450,7 +543,7 @@ def lzc_get_holds(snapname):
 
     :param bytes snapname: the name of the snapshot.
     :return: holds on the snapshot along with their creation times
-             in seconds since the epoch
+        in seconds since the epoch
     :rtype: dict of bytes : int
     '''
     holds = {}
@@ -467,38 +560,40 @@ def lzc_send(snapname, fromsnap, fd, flags=None):
 
     :param bytes snapname: the name of the snapshot to send.
     :param fromsnap: if not None the name of the starting snapshot
-                     for the incremental stream.
+        for the incremental stream.
     :type fromsnap: bytes or None
     :param int fd: the file descriptor to write the send stream to.
-    :param flags: the flags that control what enhanced features can be used
-                  in the stream.
+    :param flags: the flags that control what enhanced features can be used in
+        the stream.
     :type flags: list of bytes
 
-    :raises SnapshotNotFound: if either the starting snapshot is not `None` and does not exist,
-                              or if the ending snapshot does not exist.
+    :raises SnapshotNotFound: if either the starting snapshot is not `None` and
+        does not exist, or if the ending snapshot does not exist.
     :raises NameInvalid: if the name of either snapshot is invalid.
     :raises NameTooLong: if the name of either snapshot is too long.
-    :raises SnapshotMismatch: if ``fromsnap`` is not an ancestor snapshot of ``snapname``.
+    :raises SnapshotMismatch: if ``fromsnap`` is not an ancestor snapshot of
+        ``snapname``.
     :raises PoolsDiffer: if the snapshots belong to different pools.
     :raises IOError: if an input / output error occurs while writing to ``fd``.
-    :raises UnknownStreamFeature: if the ``flags`` contain an unknown flag name.
+    :raises UnknownStreamFeature: if the ``flags`` contain an unknown flag
+        name.
 
     If ``fromsnap`` is None, a full (non-incremental) stream will be sent.
     If ``fromsnap`` is not None, it must be the full name of a snapshot or
-    bookmark to send an incremental from, e.g. :file:`{pool}/{fs}@{earlier_snap}`
-    or :file:`{pool}/{fs}#{earlier_bmark}`.
+    bookmark to send an incremental from, e.g.
+    :file:`{pool}/{fs}@{earlier_snap}` or :file:`{pool}/{fs}#{earlier_bmark}`.
 
-    The specified snapshot or bookmark must represent an earlier point in the history
-    of ``snapname``.
-    It can be an earlier snapshot in the same filesystem or zvol as ``snapname``,
-    or it can be the origin of ``snapname``'s filesystem, or an earlier
-    snapshot in the origin, etc.
-    ``fromsnap`` must be strictly an earlier snapshot, specifying the same snapshot
-    as both ``fromsnap`` and ``snapname`` is an error.
+    The specified snapshot or bookmark must represent an earlier point in the
+    history of ``snapname``.
+    It can be an earlier snapshot in the same filesystem or zvol as
+    ``snapname``, or it can be the origin of ``snapname``'s filesystem, or an
+    earlier snapshot in the origin, etc.
+    ``fromsnap`` must be strictly an earlier snapshot, specifying the same
+    snapshot as both ``fromsnap`` and ``snapname`` is an error.
 
     If ``flags`` contains *"large_blocks"*, the stream is permitted
-    to contain ``DRR_WRITE`` records with ``drr_length`` > 128K, and ``DRR_OBJECT``
-    records with ``drr_blksz`` > 128K.
+    to contain ``DRR_WRITE`` records with ``drr_length`` > 128K,
+    and ``DRR_OBJECT`` records with ``drr_blksz`` > 128K.
 
     If ``flags`` contains *"embedded_data"*, the stream is permitted
     to contain ``DRR_WRITE_EMBEDDED`` records with
@@ -506,13 +601,24 @@ def lzc_send(snapname, fromsnap, fd, flags=None):
     which the receiving system must support (as indicated by support
     for the *embedded_data* feature).
 
+    If ``flags`` contains *"compress"*, the stream is generated by using
+    compressed WRITE records for blocks which are compressed on disk and
+    in memory.  If the *lz4_compress* feature is active on the sending
+    system, then the receiving system must have that feature enabled as well.
+
+    If ``flags`` contains *"raw"*, the stream is generated, for encrypted
+    datasets, by sending data exactly as it exists on disk.  This allows
+    backups to be taken even if encryption keys are not currently loaded.
+
     .. note::
         ``lzc_send`` can actually accept a filesystem name as the ``snapname``.
         In that case ``lzc_send`` acts as if a temporary snapshot was created
-        after the start of the call and before the stream starts being produced.
+        after the start of the call and before the stream starts being
+        produced.
 
     .. note::
-        ``lzc_send`` does not return until all of the stream is written to ``fd``.
+        ``lzc_send`` does not return until all of the stream is written to
+        ``fd``.
 
     .. note::
         ``lzc_send`` does *not* close ``fd`` upon returning.
@@ -526,8 +632,10 @@ def lzc_send(snapname, fromsnap, fd, flags=None):
         flags = []
     for flag in flags:
         c_flag = {
-            'embedded_data':    _lib.LZC_SEND_FLAG_EMBED_DATA,
-            'large_blocks':     _lib.LZC_SEND_FLAG_LARGE_BLOCK,
+            'embedded_data': _lib.LZC_SEND_FLAG_EMBED_DATA,
+            'large_blocks': _lib.LZC_SEND_FLAG_LARGE_BLOCK,
+            'compress': _lib.LZC_SEND_FLAG_COMPRESS,
+            'raw': _lib.LZC_SEND_FLAG_RAW,
         }.get(flag)
         if c_flag is None:
             raise exceptions.UnknownStreamFeature(flag)
@@ -542,27 +650,30 @@ def lzc_send_space(snapname, fromsnap=None, flags=None):
     Estimate size of a full or incremental backup stream
     given the optional starting snapshot and the ending snapshot.
 
-    :param bytes snapname: the name of the snapshot for which the estimate should be done.
+    :param bytes snapname: the name of the snapshot for which the estimate
+        should be done.
     :param fromsnap: the optional starting snapshot name.
-                     If not `None` then an incremental stream size is estimated,
-                     otherwise a full stream is esimated.
+        If not `None` then an incremental stream size is estimated, otherwise
+        a full stream is esimated.
     :type fromsnap: `bytes` or `None`
     :param flags: the flags that control what enhanced features can be used
-                  in the stream.
+        in the stream.
     :type flags: list of bytes
 
     :return: the estimated stream size, in bytes.
     :rtype: `int` or `long`
 
-    :raises SnapshotNotFound: if either the starting snapshot is not `None` and does not exist,
-                              or if the ending snapshot does not exist.
+    :raises SnapshotNotFound: if either the starting snapshot is not `None` and
+        does not exist, or if the ending snapshot does not exist.
     :raises NameInvalid: if the name of either snapshot is invalid.
     :raises NameTooLong: if the name of either snapshot is too long.
-    :raises SnapshotMismatch: if ``fromsnap`` is not an ancestor snapshot of ``snapname``.
+    :raises SnapshotMismatch: if ``fromsnap`` is not an ancestor snapshot of
+        ``snapname``.
     :raises PoolsDiffer: if the snapshots belong to different pools.
 
     ``fromsnap``, if not ``None``,  must be strictly an earlier snapshot,
-    specifying the same snapshot as both ``fromsnap`` and ``snapname`` is an error.
+    specifying the same snapshot as both ``fromsnap`` and ``snapname`` is an
+    error.
     '''
     if fromsnap is not None:
         c_fromsnap = fromsnap
@@ -573,8 +684,10 @@ def lzc_send_space(snapname, fromsnap=None, flags=None):
         flags = []
     for flag in flags:
         c_flag = {
-            'embedded_data':    _lib.LZC_SEND_FLAG_EMBED_DATA,
-            'large_blocks':     _lib.LZC_SEND_FLAG_LARGE_BLOCK,
+            'embedded_data': _lib.LZC_SEND_FLAG_EMBED_DATA,
+            'large_blocks': _lib.LZC_SEND_FLAG_LARGE_BLOCK,
+            'compress': _lib.LZC_SEND_FLAG_COMPRESS,
+            'raw': _lib.LZC_SEND_FLAG_RAW,
         }.get(flag)
         if c_flag is None:
             raise exceptions.UnknownStreamFeature(flag)
@@ -593,82 +706,88 @@ def lzc_receive(snapname, fd, force=False, raw=False, origin=None, props=None):
     :param bytes snapname: the name of the snapshot to create.
     :param int fd: the file descriptor from which to read the stream.
     :param bool force: whether to roll back or destroy the target filesystem
-                       if that is required to receive the stream.
+        if that is required to receive the stream.
     :param bool raw: whether this is a "raw" stream.
-    :param origin: the optional origin snapshot name if the stream is for a clone.
+    :param origin: the optional origin snapshot name if the stream is for a
+        clone.
     :type origin: bytes or None
-    :param props: the properties to set on the snapshot as *received* properties.
+    :param props: the properties to set on the snapshot as *received*
+        properties.
     :type props: dict of bytes : Any
 
-    :raises IOError: if an input / output error occurs while reading from the ``fd``.
+    :raises IOError: if an input / output error occurs while reading from the
+        ``fd``.
     :raises DatasetExists: if the snapshot named ``snapname`` already exists.
-    :raises DatasetExists: if the stream is a full stream and the destination filesystem already exists.
-    :raises DatasetExists: if ``force`` is `True` but the destination filesystem could not
-                           be rolled back to a matching snapshot because a newer snapshot
-                           exists and it is an origin of a cloned filesystem.
+    :raises DatasetExists: if the stream is a full stream and the destination
+        filesystem already exists.
+    :raises DatasetExists: if ``force`` is `True` but the destination
+        filesystem could not be rolled back to a matching snapshot because a
+        newer snapshot exists and it is an origin of a cloned filesystem.
     :raises StreamMismatch: if an incremental stream is received and the latest
-                            snapshot of the destination filesystem does not match
-                            the source snapshot of the stream.
+        snapshot of the destination filesystem does not match the source
+        snapshot of the stream.
     :raises StreamMismatch: if a full stream is received and the destination
-                            filesystem already exists and it has at least one snapshot,
-                            and ``force`` is `False`.
-    :raises StreamMismatch: if an incremental clone stream is received but the specified
-                            ``origin`` is not the actual received origin.
-    :raises DestinationModified: if an incremental stream is received and the destination
-                                 filesystem has been modified since the last snapshot
-                                 and ``force`` is `False`.
-    :raises DestinationModified: if a full stream is received and the destination
-                                 filesystem already exists and it does not have any
-                                 snapshots, and ``force`` is `False`.
-    :raises DatasetNotFound: if the destination filesystem and its parent do not exist.
-    :raises DatasetNotFound: if the ``origin`` is not `None` and does not exist.
-    :raises DatasetBusy: if ``force`` is `True` but the destination filesystem could not
-                         be rolled back to a matching snapshot because a newer snapshot
-                         is held and could not be destroyed.
+        filesystem already exists and it has at least one snapshot, and
+        ``force`` is `False`.
+    :raises StreamMismatch: if an incremental clone stream is received but the
+        specified ``origin`` is not the actual received origin.
+    :raises DestinationModified: if an incremental stream is received and the
+        destination filesystem has been modified since the last snapshot and
+        ``force`` is `False`.
+    :raises DestinationModified: if a full stream is received and the
+        destination filesystem already exists and it does not have any
+        snapshots, and ``force`` is `False`.
+    :raises DatasetNotFound: if the destination filesystem and its parent do
+        not exist.
+    :raises DatasetNotFound: if the ``origin`` is not `None` and does not
+        exist.
+    :raises DatasetBusy: if ``force`` is `True` but the destination filesystem
+        could not be rolled back to a matching snapshot because a newer
+        snapshot is held and could not be destroyed.
     :raises DatasetBusy: if another receive operation is being performed on the
-                         destination filesystem.
-    :raises BadStream: if the stream is corrupt or it is not recognized or it is
-                       a compound stream or it is a clone stream, but ``origin``
-                       is `None`.
-    :raises BadStream: if a clone stream is received and the destination filesystem
-                       already exists.
+        destination filesystem.
+    :raises BadStream: if the stream is corrupt or it is not recognized or it
+        is a compound stream or it is a clone stream, but ``origin`` is `None`.
+    :raises BadStream: if a clone stream is received and the destination
+        filesystem already exists.
     :raises StreamFeatureNotSupported: if the stream has a feature that is not
-                                       supported on this side.
-    :raises PropertyInvalid: if one or more of the specified properties is invalid
-                             or has an invalid type or value.
+        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
         that is not a clone stream and the destination filesystem exists.
         If the stream is a full stream and the destination filesystem does not
-        exist then the ``origin`` is checked for existence: if it does not exist
-        :exc:`.DatasetNotFound` is raised, otherwise :exc:`.StreamMismatch` is
-        raised, because that snapshot can not have any relation to the stream.
+        exist then the ``origin`` is checked for existence: if it does not
+        exist :exc:`.DatasetNotFound` is raised, otherwise
+        :exc:`.StreamMismatch` is raised, because that snapshot can not have
+        any relation to the stream.
 
     .. note::
-        If ``force`` is `True` and the stream is incremental then the destination
-        filesystem is rolled back to a matching source snapshot if necessary.
-        Intermediate snapshots are destroyed in that case.
+        If ``force`` is `True` and the stream is incremental then the
+        destination filesystem is rolled back to a matching source snapshot if
+        necessary. Intermediate snapshots are destroyed in that case.
 
         However, none of the existing snapshots may have the same name as
         ``snapname`` even if such a snapshot were to be destroyed.
-        The existing ``snapname`` snapshot always causes :exc:`.SnapshotExists`
-        to be raised.
+        The existing ``snapname`` snapshot always causes
+        :exc:`.SnapshotExists` to be raised.
 
-        If ``force`` is `True` and the stream is a full stream then the destination
-        filesystem is replaced with the received filesystem unless the former
-        has any snapshots.  This prevents the destination filesystem from being
-        rolled back / replaced.
+        If ``force`` is `True` and the stream is a full stream then the
+        destination filesystem is replaced with the received filesystem unless
+        the former has any snapshots.  This prevents the destination filesystem
+        from being rolled back / replaced.
 
     .. note::
         This interface does not work on dedup'd streams
         (those with ``DMU_BACKUP_FEATURE_DEDUP``).
 
     .. note::
-        ``lzc_receive`` does not return until all of the stream is read from ``fd``
-        and applied to the pool.
+        ``lzc_receive`` does not return until all of the stream is read from
+        ``fd`` and applied to the pool.
 
     .. note::
         ``lzc_receive`` does *not* close ``fd`` upon returning.
@@ -682,13 +801,271 @@ def lzc_receive(snapname, fd, force=False, raw=False, origin=None, props=None):
         props = {}
     nvlist = nvlist_in(props)
     ret = _lib.lzc_receive(snapname, nvlist, c_origin, force, raw, fd)
-    errors.lzc_receive_translate_error(ret, snapname, fd, force, origin, props)
+    errors.lzc_receive_translate_errors(
+        ret, snapname, fd, force, raw, False, False, origin, None
+    )
 
 
 lzc_recv = lzc_receive
 
 
-def lzc_receive_with_header(snapname, fd, header, force=False, origin=None, props=None):
+def lzc_exists(name):
+    '''
+    Check if a dataset (a filesystem, or a volume, or a snapshot)
+    with the given name exists.
+
+    :param bytes name: the dataset name to check.
+    :return: `True` if the dataset exists, `False` otherwise.
+    :rtype: bool
+
+    .. note::
+        ``lzc_exists`` can not be used to check for existence of bookmarks.
+    '''
+    ret = _lib.lzc_exists(name)
+    return bool(ret)
+
+
+@_uncommitted()
+def lzc_change_key(fsname, crypt_cmd, props=None, key=None):
+    '''
+    Change encryption key on the specified dataset.
+
+    :param bytes fsname: the name of the dataset.
+    :param str crypt_cmd: the encryption "command" to be executed, currently
+        supported values are "new_key", "inherit", "force_new_key" and
+        "force_inherit".
+    :param props: a `dict` of encryption-related property name-value pairs;
+        only "keyformat", "keylocation" and "pbkdf2iters" are supported
+        (empty by default).
+    :type props: dict of bytes:Any
+    :param key: dataset encryption key data (empty by default).
+    :type key: bytes
+
+    :raises PropertyInvalid: if ``props`` contains invalid values.
+    :raises FilesystemNotFound: if the dataset does not exist.
+    :raises UnknownCryptCommand: if ``crypt_cmd`` is invalid.
+    :raises EncryptionKeyNotLoaded: if the encryption key is not currently
+        loaded and therefore cannot be changed.
+    '''
+    if props is None:
+        props = {}
+    if key is None:
+        key = b""
+    else:
+        key = bytes(key)
+    cmd = {
+        'new_key': _lib.DCP_CMD_NEW_KEY,
+        'inherit': _lib.DCP_CMD_INHERIT,
+        'force_new_key': _lib.DCP_CMD_FORCE_NEW_KEY,
+        'force_inherit': _lib.DCP_CMD_FORCE_INHERIT,
+    }.get(crypt_cmd)
+    if cmd is None:
+        raise exceptions.UnknownCryptCommand(crypt_cmd)
+    nvlist = nvlist_in(props)
+    ret = _lib.lzc_change_key(fsname, cmd, nvlist, key, len(key))
+    errors.lzc_change_key_translate_error(ret, fsname)
+
+
+@_uncommitted()
+def lzc_load_key(fsname, noop, key):
+    '''
+    Load or verify encryption key on the specified dataset.
+
+    :param bytes fsname: the name of the dataset.
+    :param bool noop: if `True` the encryption key will only be verified,
+        not loaded.
+    :param key: dataset encryption key data.
+    :type key: bytes
+
+    :raises FilesystemNotFound: if the dataset does not exist.
+    :raises EncryptionKeyAlreadyLoaded: if the encryption key is already
+        loaded.
+    :raises EncryptionKeyInvalid: if the encryption key provided is incorrect.
+    '''
+    ret = _lib.lzc_load_key(fsname, noop, key, len(key))
+    errors.lzc_load_key_translate_error(ret, fsname, noop)
+
+
+@_uncommitted()
+def lzc_unload_key(fsname):
+    '''
+    Unload encryption key from the specified dataset.
+
+    :param bytes fsname: the name of the dataset.
+
+    :raises FilesystemNotFound: if the dataset does not exist.
+    :raises DatasetBusy: if the encryption key is still being used. This
+        usually occurs when the dataset is mounted.
+    :raises EncryptionKeyNotLoaded: if the encryption key is not currently
+        loaded.
+    '''
+    ret = _lib.lzc_unload_key(fsname)
+    errors.lzc_unload_key_translate_error(ret, fsname)
+
+
+def lzc_channel_program(
+    poolname, program, instrlimit=ZCP_DEFAULT_INSTRLIMIT,
+    memlimit=ZCP_DEFAULT_MEMLIMIT, params=None
+):
+    '''
+    Executes a script as a ZFS channel program on pool ``poolname``.
+
+    :param bytes poolname: the name of the pool.
+    :param bytes program: channel program text.
+    :param int instrlimit: execution time limit, in milliseconds.
+    :param int memlimit: execution memory limit, in bytes.
+    :param bytes params: a `list` of parameters passed to the channel program
+        (empty by default).
+    :type params: dict of bytes:Any
+    :return: a dictionary of result values procuced by the channel program,
+        if any.
+    :rtype: dict
+
+    :raises PoolNotFound: if the pool does not exist.
+    :raises ZCPLimitInvalid: if either instruction or memory limit are invalid.
+    :raises ZCPSyntaxError: if the channel program contains syntax errors.
+    :raises ZCPTimeout: if the channel program took too long to execute.
+    :raises ZCPSpaceError: if the channel program exhausted the memory limit.
+    :raises ZCPMemoryError: if the channel program return value was too large.
+    :raises ZCPPermissionError: if the user lacks the permission to run the
+        channel program. Channel programs must be run as root.
+    :raises ZCPRuntimeError: if the channel program encountered a runtime
+        error.
+    '''
+    output = {}
+    params_nv = nvlist_in({b"argv": params})
+    with nvlist_out(output) as outnvl:
+        ret = _lib.lzc_channel_program(
+            poolname, program, instrlimit, memlimit, params_nv, outnvl)
+    errors.lzc_channel_program_translate_error(
+        ret, poolname, output.get(b"error"))
+    return output.get(b"return")
+
+
+def lzc_channel_program_nosync(
+    poolname, program, instrlimit=ZCP_DEFAULT_INSTRLIMIT,
+    memlimit=ZCP_DEFAULT_MEMLIMIT, params=None
+):
+    '''
+    Executes a script as a read-only ZFS channel program on pool ``poolname``.
+    A read-only channel program works programmatically the same way as a
+    normal channel program executed with
+    :func:`lzc_channel_program`. The only difference is it runs exclusively in
+    open-context and therefore can return faster.
+    The downside to that, is that the program cannot change on-disk state by
+    calling functions from the zfs.sync submodule.
+
+    :param bytes poolname: the name of the pool.
+    :param bytes program: channel program text.
+    :param int instrlimit: execution time limit, in milliseconds.
+    :param int memlimit: execution memory limit, in bytes.
+    :param bytes params: a `list` of parameters passed to the channel program
+        (empty by default).
+    :type params: dict of bytes:Any
+    :return: a dictionary of result values procuced by the channel program,
+        if any.
+    :rtype: dict
+
+    :raises PoolNotFound: if the pool does not exist.
+    :raises ZCPLimitInvalid: if either instruction or memory limit are invalid.
+    :raises ZCPSyntaxError: if the channel program contains syntax errors.
+    :raises ZCPTimeout: if the channel program took too long to execute.
+    :raises ZCPSpaceError: if the channel program exhausted the memory limit.
+    :raises ZCPMemoryError: if the channel program return value was too large.
+    :raises ZCPPermissionError: if the user lacks the permission to run the
+        channel program. Channel programs must be run as root.
+    :raises ZCPRuntimeError: if the channel program encountered a runtime
+        error.
+    '''
+    output = {}
+    params_nv = nvlist_in({b"argv": params})
+    with nvlist_out(output) as outnvl:
+        ret = _lib.lzc_channel_program_nosync(
+            poolname, program, instrlimit, memlimit, params_nv, outnvl)
+    errors.lzc_channel_program_translate_error(
+        ret, poolname, output.get(b"error"))
+    return output.get(b"return")
+
+
+def lzc_receive_resumable(
+    snapname, fd, force=False, raw=False, origin=None, props=None
+):
+    '''
+    Like :func:`lzc_receive`, but if the receive fails due to premature stream
+    termination, the intermediate state will be preserved on disk.  In this
+    case, ECKSUM will be returned.  The receive may subsequently be resumed
+    with a resuming send stream generated by lzc_send_resume().
+
+    :param bytes snapname: the name of the snapshot to create.
+    :param int fd: the file descriptor from which to read the stream.
+    :param bool force: whether to roll back or destroy the target filesystem
+        if that is required to receive the stream.
+    :param bool raw: whether this is a "raw" stream.
+    :param origin: the optional origin snapshot name if the stream is for a
+        clone.
+    :type origin: bytes or None
+    :param props: the properties to set on the snapshot as *received*
+        properties.
+    :type props: dict of bytes : Any
+
+    :raises IOError: if an input / output error occurs while reading from the
+        ``fd``.
+    :raises DatasetExists: if the snapshot named ``snapname`` already exists.
+    :raises DatasetExists: if the stream is a full stream and the destination
+        filesystem already exists.
+    :raises DatasetExists: if ``force`` is `True` but the destination
+        filesystem could not be rolled back to a matching snapshot because a
+        newer snapshot exists and it is an origin of a cloned filesystem.
+    :raises StreamMismatch: if an incremental stream is received and the latest
+        snapshot of the destination filesystem does not match the source
+        snapshot of the stream.
+    :raises StreamMismatch: if a full stream is received and the destination
+        filesystem already exists and it has at least one snapshot, and
+        ``force`` is `False`.
+    :raises StreamMismatch: if an incremental clone stream is received but the
+        specified ``origin`` is not the actual received origin.
+    :raises DestinationModified: if an incremental stream is received and the
+        destination filesystem has been modified since the last snapshot and
+        ``force`` is `False`.
+    :raises DestinationModified: if a full stream is received and the
+        destination filesystem already exists and it does not have any
+        snapshots, and ``force`` is `False`.
+    :raises DatasetNotFound: if the destination filesystem and its parent do
+        not exist.
+    :raises DatasetNotFound: if the ``origin`` is not `None` and does not
+        exist.
+    :raises DatasetBusy: if ``force`` is `True` but the destination filesystem
+        could not be rolled back to a matching snapshot because a newer
+        snapshot is held and could not be destroyed.
+    :raises DatasetBusy: if another receive operation is being performed on the
+        destination filesystem.
+    :raises BadStream: if the stream is corrupt or it is not recognized or it
+        is a compound stream or it is a clone stream, but ``origin`` is `None`.
+    :raises BadStream: if a clone stream is received and the destination
+        filesystem already exists.
+    :raises StreamFeatureNotSupported: if the stream has a feature that is not
+        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.
+    '''
+
+    if origin is not None:
+        c_origin = origin
+    else:
+        c_origin = _ffi.NULL
+    if props is None:
+        props = {}
+    nvlist = nvlist_in(props)
+    ret = _lib.lzc_receive_resumable(
+        snapname, nvlist, c_origin, force, raw, fd)
+    errors.lzc_receive_translate_errors(
+        ret, snapname, fd, force, raw, False, False, origin, None)
+
+
+def lzc_receive_with_header(
+    snapname, fd, begin_record, force=False, resumable=False, raw=False,
+    origin=None, props=None
+):
     '''
     Like :func:`lzc_receive`, but allows the caller to read the begin record
     and then to pass it in.
@@ -696,56 +1073,65 @@ def lzc_receive_with_header(snapname, fd, header, force=False, origin=None, prop
     That could be useful if the caller wants to derive, for example,
     the snapname or the origin parameters based on the information contained in
     the begin record.
-    :func:`receive_header` can be used to receive the begin record from the file
-    descriptor.
+    :func:`receive_header` can be used to receive the begin record from the
+    file descriptor.
 
     :param bytes snapname: the name of the snapshot to create.
     :param int fd: the file descriptor from which to read the stream.
-    :param header: the stream's begin header.
-    :type header: ``cffi`` `CData` representing the header structure.
+    :param begin_record: the stream's begin record.
+    :type begin_record: ``cffi`` `CData` representing the dmu_replay_record_t
+        structure.
     :param bool force: whether to roll back or destroy the target filesystem
-                       if that is required to receive the stream.
-    :param origin: the optional origin snapshot name if the stream is for a clone.
+        if that is required to receive the stream.
+    :param bool resumable: whether this stream should be treated as resumable.
+        If the receive fails due to premature stream termination, the
+        intermediate state will be preserved on disk and may subsequently be
+        resumed with :func:`lzc_send_resume`.
+    :param bool raw: whether this is a "raw" stream.
+    :param origin: the optional origin snapshot name if the stream is for a
+        clone.
     :type origin: bytes or None
-    :param props: the properties to set on the snapshot as *received* properties.
+    :param props: the properties to set on the snapshot as *received*
+        properties.
     :type props: dict of bytes : Any
 
-    :raises IOError: if an input / output error occurs while reading from the ``fd``.
+    :raises IOError: if an input / output error occurs while reading from the
+        ``fd``.
     :raises DatasetExists: if the snapshot named ``snapname`` already exists.
-    :raises DatasetExists: if the stream is a full stream and the destination filesystem already exists.
-    :raises DatasetExists: if ``force`` is `True` but the destination filesystem could not
-                           be rolled back to a matching snapshot because a newer snapshot
-                           exists and it is an origin of a cloned filesystem.
+    :raises DatasetExists: if the stream is a full stream and the destination
+        filesystem already exists.
+    :raises DatasetExists: if ``force`` is `True` but the destination
+        filesystem could not be rolled back to a matching snapshot because a
+        newer snapshot exists and it is an origin of a cloned filesystem.
     :raises StreamMismatch: if an incremental stream is received and the latest
-                            snapshot of the destination filesystem does not match
-                            the source snapshot of the stream.
+        snapshot of the destination filesystem does not match the source
+        snapshot of the stream.
     :raises StreamMismatch: if a full stream is received and the destination
-                            filesystem already exists and it has at least one snapshot,
-                            and ``force`` is `False`.
-    :raises StreamMismatch: if an incremental clone stream is received but the specified
-                            ``origin`` is not the actual received origin.
-    :raises DestinationModified: if an incremental stream is received and the destination
-                                 filesystem has been modified since the last snapshot
-                                 and ``force`` is `False`.
-    :raises DestinationModified: if a full stream is received and the destination
-                                 filesystem already exists and it does not have any
-                                 snapshots, and ``force`` is `False`.
-    :raises DatasetNotFound: if the destination filesystem and its parent do not exist.
-    :raises DatasetNotFound: if the ``origin`` is not `None` and does not exist.
-    :raises DatasetBusy: if ``force`` is `True` but the destination filesystem could not
-                         be rolled back to a matching snapshot because a newer snapshot
-                         is held and could not be destroyed.
+        filesystem already exists and it has at least one snapshot, and
+        ``force`` is `False`.
+    :raises StreamMismatch: if an incremental clone stream is received but the
+        specified ``origin`` is not the actual received origin.
+    :raises DestinationModified: if an incremental stream is received and the
+        destination filesystem has been modified since the last snapshot and
+        ``force`` is `False`.
+    :raises DestinationModified: if a full stream is received and the
+        destination filesystem already exists and it does not have any
+        snapshots, and ``force`` is `False`.
+    :raises DatasetNotFound: if the destination filesystem and its parent do
+        not exist.
+    :raises DatasetNotFound: if the ``origin`` is not `None` and does not
+        exist.
+    :raises DatasetBusy: if ``force`` is `True` but the destination filesystem
+        could not be rolled back to a matching snapshot because a newer
+        snapshot is held and could not be destroyed.
     :raises DatasetBusy: if another receive operation is being performed on the
-                         destination filesystem.
-    :raises BadStream: if the stream is corrupt or it is not recognized or it is
-                       a compound stream or it is a clone stream, but ``origin``
-                       is `None`.
-    :raises BadStream: if a clone stream is received and the destination filesystem
-                       already exists.
+        destination filesystem.
+    :raises BadStream: if the stream is corrupt or it is not recognized or it
+        is a compound stream or it is a clone stream, but ``origin`` is `None`.
+    :raises BadStream: if a clone stream is received and the destination
+        filesystem already exists.
     :raises StreamFeatureNotSupported: if the stream has a feature that is not
-                                       supported on this side.
-    :raises PropertyInvalid: if one or more of the specified properties is invalid
-                             or has an invalid type or value.
+        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.
     '''
@@ -757,22 +1143,25 @@ def lzc_receive_with_header(snapname, fd, header, force=False, origin=None, prop
     if props is None:
         props = {}
     nvlist = nvlist_in(props)
-    ret = _lib.lzc_receive_with_header(snapname, nvlist, c_origin, force,
-                                       False, fd, header)
-    errors.lzc_receive_translate_error(ret, snapname, fd, force, origin, props)
+    ret = _lib.lzc_receive_with_header(
+        snapname, nvlist, c_origin, force, resumable, raw, fd, begin_record)
+    errors.lzc_receive_translate_errors(
+        ret, snapname, fd, force, raw, False, False, origin, None)
 
 
 def receive_header(fd):
     '''
-    Read the begin record of the ZFS backup stream from the given file descriptor.
+    Read the begin record of the ZFS backup stream from the given file
+    descriptor.
 
     This is a helper function for :func:`lzc_receive_with_header`.
 
     :param int fd: the file descriptor from which to read the stream.
-    :return: a tuple with two elements where the first one is a Python `dict` representing
-             the fields of the begin record and the second one is an opaque object
-             suitable for passing to :func:`lzc_receive_with_header`.
-    :raises IOError: if an input / output error occurs while reading from the ``fd``.
+    :return: a tuple with two elements where the first one is a Python `dict`
+        representing the fields of the begin record and the second one is an
+        opaque object suitable for passing to :func:`lzc_receive_with_header`.
+    :raises IOError: if an input / output error occurs while reading from the
+        ``fd``.
 
     At present the following fields can be of interest in the header:
 
@@ -781,15 +1170,16 @@ def receive_header(fd):
     drr_toguid : integer
         the GUID of the snapshot for which the stream has been created
     drr_fromguid : integer
-        the GUID of the starting snapshot in the case the stream is incremental,
-        zero otherwise
+        the GUID of the starting snapshot in the case the stream is
+        incremental, zero otherwise
     drr_flags : integer
         the flags describing the stream's properties
     drr_type : integer
         the type of the dataset for which the stream has been created
         (volume, filesystem)
     '''
-    # read sizeof(dmu_replay_record_t) bytes directly into the memort backing 'record'
+    # read sizeof(dmu_replay_record_t) bytes directly into the memort backing
+    # 'record'
     record = _ffi.new("dmu_replay_record_t *")
     _ffi.buffer(record)[:] = os.read(fd, _ffi.sizeof(record[0]))
     # get drr_begin member and its representation as a Pythn dict
@@ -803,24 +1193,330 @@ def receive_header(fd):
         elif descr.type.kind == 'array' and descr.type.item.cname == 'char':
             header[field] = _ffi.string(getattr(drr_begin, field))
         else:
-            raise TypeError('Unexpected field type in drr_begin: ' + str(descr.type))
+            raise TypeError(
+                'Unexpected field type in drr_begin: ' + str(descr.type))
     return (header, record)
 
 
-def lzc_exists(name):
+@_uncommitted()
+def lzc_receive_one(
+    snapname, fd, begin_record, force=False, resumable=False, raw=False,
+    origin=None, props=None, cleanup_fd=-1, action_handle=0
+):
     '''
-    Check if a dataset (a filesystem, or a volume, or a snapshot)
-    with the given name exists.
+    Like :func:`lzc_receive`, but allows the caller to pass all supported
+    arguments and retrieve all values returned.  The only additional input
+    parameter is 'cleanup_fd' which is used to set a cleanup-on-exit file
+    descriptor.
 
-    :param bytes name: the dataset name to check.
-    :return: `True` if the dataset exists, `False` otherwise.
-    :rtype: bool
+    :param bytes snapname: the name of the snapshot to create.
+    :param int fd: the file descriptor from which to read the stream.
+    :param begin_record: the stream's begin record.
+    :type begin_record: ``cffi`` `CData` representing the dmu_replay_record_t
+        structure.
+    :param bool force: whether to roll back or destroy the target filesystem
+        if that is required to receive the stream.
+    :param bool resumable: whether this stream should be treated as resumable.
+        If the receive fails due to premature stream termination, the
+        intermediate state will be preserved on disk and may subsequently be
+        resumed with :func:`lzc_send_resume`.
+    :param bool raw: whether this is a "raw" stream.
+    :param origin: the optional origin snapshot name if the stream is for a
+        clone.
+    :type origin: bytes or None
+    :param props: the properties to set on the snapshot as *received*
+        properties.
+    :type props: dict of bytes : Any
+    :param int cleanup_fd: file descriptor used to set a cleanup-on-exit file
+        descriptor.
+    :param int action_handle: variable used to pass the handle for guid/ds
+        mapping: this should be set to zero on first call and will contain an
+        updated handle on success, which should be passed in subsequent calls.
+
+    :return: a tuple with two elements where the first one is the number of
+        bytes read from the file descriptor and the second one is the
+        action_handle return value.
+
+    :raises IOError: if an input / output error occurs while reading from the
+        ``fd``.
+    :raises DatasetExists: if the snapshot named ``snapname`` already exists.
+    :raises DatasetExists: if the stream is a full stream and the destination
+        filesystem already exists.
+    :raises DatasetExists: if ``force`` is `True` but the destination
+        filesystem could not be rolled back to a matching snapshot because a
+        newer snapshot exists and it is an origin of a cloned filesystem.
+    :raises StreamMismatch: if an incremental stream is received and the latest
+        snapshot of the destination filesystem does not match the source
+        snapshot of the stream.
+    :raises StreamMismatch: if a full stream is received and the destination
+        filesystem already exists and it has at least one snapshot, and
+        ``force`` is `False`.
+    :raises StreamMismatch: if an incremental clone stream is received but the
+        specified ``origin`` is not the actual received origin.
+    :raises DestinationModified: if an incremental stream is received and the
+        destination filesystem has been modified since the last snapshot and
+        ``force`` is `False`.
+    :raises DestinationModified: if a full stream is received and the
+        destination filesystem already exists and it does not have any
+        snapshots, and ``force`` is `False`.
+    :raises DatasetNotFound: if the destination filesystem and its parent do
+        not exist.
+    :raises DatasetNotFound: if the ``origin`` is not `None` and does not
+        exist.
+    :raises DatasetBusy: if ``force`` is `True` but the destination filesystem
+        could not be rolled back to a matching snapshot because a newer
+        snapshot is held and could not be destroyed.
+    :raises DatasetBusy: if another receive operation is being performed on the
+        destination filesystem.
+    :raises BadStream: if the stream is corrupt or it is not recognized or it
+        is a compound stream or it is a clone stream, but ``origin`` is `None`.
+    :raises BadStream: if a clone stream is received and the destination
+        filesystem already exists.
+    :raises StreamFeatureNotSupported: if the stream has a feature that is not
+        supported on this side.
+    :raises ReceivePropertyFailure: if one or more of the specified properties
+        is invalid or has an invalid type or value.
+    :raises NameInvalid: if the name of either snapshot is invalid.
+    :raises NameTooLong: if the name of either snapshot is too long.
+    '''
+
+    if origin is not None:
+        c_origin = origin
+    else:
+        c_origin = _ffi.NULL
+    if action_handle is not None:
+        c_action_handle = _ffi.new("uint64_t *")
+    else:
+        c_action_handle = _ffi.NULL
+    c_read_bytes = _ffi.new("uint64_t *")
+    c_errflags = _ffi.new("uint64_t *")
+    if props is None:
+        props = {}
+    nvlist = nvlist_in(props)
+    properrs = {}
+    with nvlist_out(properrs) as c_errors:
+        ret = _lib.lzc_receive_one(
+            snapname, nvlist, c_origin, force, resumable, raw, fd,
+            begin_record, cleanup_fd, c_read_bytes, c_errflags,
+            c_action_handle, c_errors)
+    errors.lzc_receive_translate_errors(
+        ret, snapname, fd, force, raw, False, False, origin, properrs)
+    return (int(c_read_bytes[0]), action_handle)
+
+
+@_uncommitted()
+def lzc_receive_with_cmdprops(
+    snapname, fd, begin_record, force=False, resumable=False, raw=False,
+    origin=None, props=None, cmdprops=None, key=None, cleanup_fd=-1,
+    action_handle=0
+):
+    '''
+    Like :func:`lzc_receive_one`, but allows the caller to pass an additional
+    'cmdprops' argument. The 'cmdprops' nvlist contains both override
+    ('zfs receive -o') and exclude ('zfs receive -x') properties.
+
+    :param bytes snapname: the name of the snapshot to create.
+    :param int fd: the file descriptor from which to read the stream.
+    :param begin_record: the stream's begin record.
+    :type begin_record: ``cffi`` `CData` representing the dmu_replay_record_t
+        structure.
+    :param bool force: whether to roll back or destroy the target filesystem
+        if that is required to receive the stream.
+    :param bool resumable: whether this stream should be treated as resumable.
+        If the receive fails due to premature stream termination, the
+        intermediate state will be preserved on disk and may subsequently be
+        resumed with :func:`lzc_send_resume`.
+    :param bool raw: whether this is a "raw" stream.
+    :param origin: the optional origin snapshot name if the stream is for a
+        clone.
+    :type origin: bytes or None
+    :param props: the properties to set on the snapshot as *received*
+        properties.
+    :type props: dict of bytes : Any
+    :param cmdprops: the properties to set on the snapshot as local overrides
+        to *received* properties. `bool` values are forcefully inherited while
+        every other value is set locally as if the command "zfs set" was
+        invoked immediately before the receive.
+    :type cmdprops: dict of bytes : Any
+    :param key: raw bytes representing user's wrapping key
+    :type key: bytes
+    :param int cleanup_fd: file descriptor used to set a cleanup-on-exit file
+        descriptor.
+    :param int action_handle: variable used to pass the handle for guid/ds
+        mapping: this should be set to zero on first call and will contain an
+        updated handle on success, it should be passed in subsequent calls.
+
+    :return: a tuple with two elements where the first one is the number of
+        bytes read from the file descriptor and the second one is the
+        action_handle return value.
+
+    :raises IOError: if an input / output error occurs while reading from the
+        ``fd``.
+    :raises DatasetExists: if the snapshot named ``snapname`` already exists.
+    :raises DatasetExists: if the stream is a full stream and the destination
+        filesystem already exists.
+    :raises DatasetExists: if ``force`` is `True` but the destination
+        filesystem could not be rolled back to a matching snapshot because a
+        newer snapshot exists and it is an origin of a cloned filesystem.
+    :raises StreamMismatch: if an incremental stream is received and the latest
+        snapshot of the destination filesystem does not match the source
+        snapshot of the stream.
+    :raises StreamMismatch: if a full stream is received and the destination
+        filesystem already exists and it has at least one snapshot, and
+        ``force`` is `False`.
+    :raises StreamMismatch: if an incremental clone stream is received but the
+        specified ``origin`` is not the actual received origin.
+    :raises DestinationModified: if an incremental stream is received and the
+        destination filesystem has been modified since the last snapshot and
+        ``force`` is `False`.
+    :raises DestinationModified: if a full stream is received and the
+        destination filesystem already exists and it does not have any
+        snapshots, and ``force`` is `False`.
+    :raises DatasetNotFound: if the destination filesystem and its parent do
+        not exist.
+    :raises DatasetNotFound: if the ``origin`` is not `None` and does not
+        exist.
+    :raises DatasetBusy: if ``force`` is `True` but the destination filesystem
+        could not be rolled back to a matching snapshot because a newer
+        snapshot is held and could not be destroyed.
+    :raises DatasetBusy: if another receive operation is being performed on the
+        destination filesystem.
+    :raises BadStream: if the stream is corrupt or it is not recognized or it
+        is a compound stream or it is a clone stream, but ``origin`` is `None`.
+    :raises BadStream: if a clone stream is received and the destination
+        filesystem already exists.
+    :raises StreamFeatureNotSupported: if the stream has a feature that is not
+        supported on this side.
+    :raises ReceivePropertyFailure: if one or more of the specified properties
+        is invalid or has an invalid type or value.
+    :raises NameInvalid: if the name of either snapshot is invalid.
+    :raises NameTooLong: if the name of either snapshot is too long.
+    '''
+
+    if origin is not None:
+        c_origin = origin
+    else:
+        c_origin = _ffi.NULL
+    if action_handle is not None:
+        c_action_handle = _ffi.new("uint64_t *")
+    else:
+        c_action_handle = _ffi.NULL
+    c_read_bytes = _ffi.new("uint64_t *")
+    c_errflags = _ffi.new("uint64_t *")
+    if props is None:
+        props = {}
+    if cmdprops is None:
+        cmdprops = {}
+    if key is None:
+        key = b""
+    else:
+        key = bytes(key)
+
+    nvlist = nvlist_in(props)
+    cmdnvlist = nvlist_in(cmdprops)
+    properrs = {}
+    with nvlist_out(properrs) as c_errors:
+        ret = _lib.lzc_receive_with_cmdprops(
+            snapname, nvlist, cmdnvlist, key, len(key), c_origin,
+            force, resumable, raw, fd, begin_record, cleanup_fd, c_read_bytes,
+            c_errflags, c_action_handle, c_errors)
+    errors.lzc_receive_translate_errors(
+        ret, snapname, fd, force, raw, False, False, origin, properrs)
+    return (int(c_read_bytes[0]), action_handle)
+
+
+@_uncommitted()
+def lzc_reopen(poolname, restart=True):
+    '''
+    Reopen a pool
+
+    :param bytes poolname: the name of the pool.
+    :param bool restart: whether to restart an in-progress scrub operation.
+
+    :raises PoolNotFound: if the pool does not exist.
+    '''
+    ret = _lib.lzc_reopen(poolname, restart)
+    errors.lzc_reopen_translate_error(ret, poolname)
+
+
+def lzc_send_resume(
+    snapname, fromsnap, fd, flags=None, resumeobj=0, resumeoff=0
+):
+    '''
+    Resume a previously interrupted send operation generating a zfs send stream
+    for the specified snapshot and writing it to the specified file descriptor.
+
+    :param bytes snapname: the name of the snapshot to send.
+    :param fromsnap: if not None the name of the starting snapshot
+        for the incremental stream.
+    :type fromsnap: bytes or None
+    :param int fd: the file descriptor to write the send stream to.
+    :param flags: the flags that control what enhanced features can be used in
+        the stream.
+    :type flags: list of bytes
+    :param int resumeobj: the object number where this send stream should
+        resume from.
+    :param int resumeoff: the offset where this send stream should resume from.
+
+    :raises SnapshotNotFound: if either the starting snapshot is not `None` and
+        does not exist, or if the ending snapshot does not exist.
+    :raises NameInvalid: if the name of either snapshot is invalid.
+    :raises NameTooLong: if the name of either snapshot is too long.
+    :raises SnapshotMismatch: if ``fromsnap`` is not an ancestor snapshot of
+        ``snapname``.
+    :raises PoolsDiffer: if the snapshots belong to different pools.
+    :raises IOError: if an input / output error occurs while writing to ``fd``.
+    :raises UnknownStreamFeature: if the ``flags`` contain an unknown flag
+        name.
 
     .. note::
-        ``lzc_exists`` can not be used to check for existence of bookmarks.
+        See :func:`lzc_send` for more information.
     '''
-    ret = _lib.lzc_exists(name)
-    return bool(ret)
+    if fromsnap is not None:
+        c_fromsnap = fromsnap
+    else:
+        c_fromsnap = _ffi.NULL
+    c_flags = 0
+    if flags is None:
+        flags = []
+    for flag in flags:
+        c_flag = {
+            'embedded_data': _lib.LZC_SEND_FLAG_EMBED_DATA,
+            'large_blocks': _lib.LZC_SEND_FLAG_LARGE_BLOCK,
+            'compress': _lib.LZC_SEND_FLAG_COMPRESS,
+            'raw': _lib.LZC_SEND_FLAG_RAW,
+        }.get(flag)
+        if c_flag is None:
+            raise exceptions.UnknownStreamFeature(flag)
+        c_flags |= c_flag
+
+    ret = _lib.lzc_send_resume(
+        snapname, c_fromsnap, fd, c_flags, uint64_t(resumeobj),
+        uint64_t(resumeoff))
+    errors.lzc_send_translate_error(ret, snapname, fromsnap, fd, flags)
+
+
+@_uncommitted()
+def lzc_sync(poolname, force=False):
+    '''
+    Forces all in-core dirty data to be written to the primary pool storage
+    and not the ZIL.
+
+    :param bytes poolname: the name of the pool.
+    :param bool force: whether to force uberblock update even if there is no
+        dirty data.
+
+    :raises PoolNotFound: if the pool does not exist.
+
+    .. note::
+        This method signature is different from its C libzfs_core counterpart:
+        `innvl` has been replaced by the `force` boolean and `outnvl` has been
+        conveniently removed since it's not used.
+    '''
+    innvl = nvlist_in({b"force": force})
+    with nvlist_out({}) as outnvl:
+        ret = _lib.lzc_sync(poolname, innvl, outnvl)
+    errors.lzc_sync_translate_error(ret, poolname)
 
 
 def is_supported(func):
@@ -847,40 +1543,6 @@ def is_supported(func):
     return getattr(_lib, fname, None) is not None
 
 
-def _uncommitted(depends_on=None):
-    '''
-    Mark an API function as being an uncommitted extension that might not be
-    available.
-
-    :param function depends_on: the function that would be checked
-                                instead of a decorated function.
-                                For example, if the decorated function uses
-                                another uncommitted function.
-
-    This decorator transforms a decorated function to raise
-    :exc:`NotImplementedError` if the C libzfs_core library does not provide
-    a function with the same name as the decorated function.
-
-    The optional `depends_on` parameter can be provided if the decorated
-    function does not directly call the C function but instead calls another
-    Python function that follows the typical convention.
-    One example is :func:`lzc_list_snaps` that calls :func:`lzc_list` that
-    calls ``lzc_list`` in libzfs_core.
-
-    This decorator is implemented using :func:`is_supported`.
-    '''
-    def _uncommitted_decorator(func, depends_on=depends_on):
-        @functools.wraps(func)
-        def _f(*args, **kwargs):
-            if not is_supported(_f):
-                raise NotImplementedError(func.__name__)
-            return func(*args, **kwargs)
-        if depends_on is not None:
-            _f._check_func = depends_on
-        return _f
-    return _uncommitted_decorator
-
-
 @_uncommitted()
 def lzc_promote(name):
     '''
@@ -889,19 +1551,48 @@ def lzc_promote(name):
     :param bytes name: the name of the dataset to promote.
     :raises NameInvalid: if the dataset name is invalid.
     :raises NameTooLong: if the dataset name is too long.
-    :raises NameTooLong: if the dataset's origin has a snapshot that,
-                         if transferred to the dataset, would get
-                         a too long name.
+    :raises NameTooLong: if the dataset's origin has a snapshot that, if
+        transferred to the dataset, would get a too long name.
     :raises NotClone: if the dataset is not a clone.
     :raises FilesystemNotFound: if the dataset does not exist.
-    :raises SnapshotExists: if the dataset already has a snapshot with
-                            the same name as one of the origin's snapshots.
+    :raises SnapshotExists: if the dataset already has a snapshot with the same
+        name as one of the origin's snapshots.
     '''
     ret = _lib.lzc_promote(name, _ffi.NULL, _ffi.NULL)
     errors.lzc_promote_translate_error(ret, name)
 
 
 @_uncommitted()
+def lzc_pool_checkpoint(name):
+    '''
+    Creates a checkpoint for the specified pool.
+
+    :param bytes name: the name of the pool to create a checkpoint for.
+    :raises CheckpointExists: if the pool already has a checkpoint.
+    :raises CheckpointDiscarding: if ZFS is in the middle of discarding a
+        checkpoint for this pool.
+    :raises DeviceRemovalRunning: if a vdev is currently being removed.
+    :raises DeviceTooBig: if one or more top-level vdevs exceed the maximum
+        vdev size.
+    '''
+    ret = _lib.lzc_pool_checkpoint(name)
+    errors.lzc_pool_checkpoint_translate_error(ret, name)
+
+
+@_uncommitted()
+def lzc_pool_checkpoint_discard(name):
+    '''
+    Discard the checkpoint from the specified pool.
+
+    :param bytes name: the name of the pool to discard the checkpoint from.
+    :raises CheckpointNotFound: if pool does not have a checkpoint.
+    :raises CheckpointDiscarding: if ZFS is in the middle of discarding a
+        checkpoint for this pool.
+    '''
+    ret = _lib.lzc_pool_checkpoint_discard(name)
+    errors.lzc_pool_checkpoint_discard_translate_error(ret, name)
+
+
 def lzc_rename(source, target):
     '''
     Rename the ZFS dataset.
@@ -910,19 +1601,20 @@ def lzc_rename(source, target):
     :param target name: the new name of the dataset.
     :raises NameInvalid: if either the source or target name is invalid.
     :raises NameTooLong: if either the source or target name is too long.
-    :raises NameTooLong: if a snapshot of the source would get a too long
-                         name after renaming.
+    :raises NameTooLong: if a snapshot of the source would get a too long name
+        after renaming.
     :raises FilesystemNotFound: if the source does not exist.
     :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, _ffi.NULL, _ffi.NULL)
+    ret = _lib.lzc_rename(source, target)
     errors.lzc_rename_translate_error(ret, source, target)
 
 
-@_uncommitted()
-def lzc_destroy_one(name):
+def lzc_destroy(name):
     '''
     Destroy the ZFS dataset.
 
@@ -931,16 +1623,10 @@ def lzc_destroy_one(name):
     :raises NameTooLong: if the dataset name is too long.
     :raises FilesystemNotFound: if the dataset does not exist.
     '''
-    ret = _lib.lzc_destroy_one(name, _ffi.NULL)
+    ret = _lib.lzc_destroy(name)
     errors.lzc_destroy_translate_error(ret, name)
 
 
-# As the extended API is not committed yet, the names of the new interfaces
-# are not settled down yet.
-# lzc_destroy() might make more sense as we do not have lzc_create_one().
-lzc_destroy = lzc_destroy_one
-
-
 @_uncommitted()
 def lzc_inherit(name, prop):
     '''
@@ -951,8 +1637,8 @@ def lzc_inherit(name, prop):
     :raises NameInvalid: if the dataset name is invalid.
     :raises NameTooLong: if the dataset name is too long.
     :raises DatasetNotFound: if the dataset does not exist.
-    :raises PropertyInvalid: if one or more of the specified properties is invalid
-                             or has an invalid type or value.
+    :raises PropertyInvalid: if one or more of the specified properties is
+        invalid or has an invalid type or value.
 
     Inheriting a property actually resets it to its default value
     or removes it if it's a user property, so that the property could be
@@ -982,10 +1668,10 @@ def lzc_set_props(name, prop, val):
     :raises NameInvalid: if the dataset name is invalid.
     :raises NameTooLong: if the dataset name is too long.
     :raises DatasetNotFound: if the dataset does not exist.
-    :raises NoSpace: if the property controls a quota and the values is
-                     too small for that quota.
-    :raises PropertyInvalid: if one or more of the specified properties is invalid
-                             or has an invalid type or value.
+    :raises NoSpace: if the property controls a quota and the values is too
+        small for that quota.
+    :raises PropertyInvalid: if one or more of the specified properties is
+        invalid or has an invalid type or value.
 
     This function can be used on snapshots to set user defined properties.
 
@@ -1013,27 +1699,25 @@ def lzc_list(name, options):
     '''
     List subordinate elements of the given dataset.
 
-    This function can be used to list child datasets and snapshots
-    of the given dataset.  The listed elements can be filtered by
-    their type and by their depth relative to the starting dataset.
+    This function can be used to list child datasets and snapshots of the given
+    dataset.  The listed elements can be filtered by their type and by their
+    depth relative to the starting dataset.
 
-    :param bytes name: the name of the dataset to be listed, could
-                       be a snapshot or a dataset.
-    :param options: a `dict` of the options that control the listing
-                    behavior.
+    :param bytes name: the name of the dataset to be listed, could be a
+        snapshot or a dataset.
+    :param options: a `dict` of the options that control the listing behavior.
     :type options: dict of bytes:Any
-    :return: a pair of file descriptors the first of which can be
-             used to read the listing.
+    :return: a pair of file descriptors the first of which can be used to read
+        the listing.
     :rtype: tuple of (int, int)
     :raises DatasetNotFound: if the dataset does not exist.
 
     Two options are currently available:
 
     recurse : integer or None
-        specifies depth of the recursive listing. If ``None`` the
-        depth is not limited.
-        Absence of this option means that only the given dataset
-        is listed.
+        specifies depth of the recursive listing. If ``None`` the depth is not
+        limited.
+        Absence of this option means that only the given dataset is listed.
 
     type : dict of bytes:None
         specifies dataset types to include into the listing.
@@ -1077,18 +1761,16 @@ def _list(name, recurse=None, types=None):
     with the file descriptors and provides data in an easy to
     consume format.
 
-    :param bytes name: the name of the dataset to be listed, could
-                       be a snapshot, a volume or a filesystem.
-    :param recurse: specifies depth of the recursive listing.
-                    If ``None`` the depth is not limited.
+    :param bytes name: the name of the dataset to be listed, could be a
+        snapshot, a volume or a filesystem.
+    :param recurse: specifies depth of the recursive listing. If ``None`` the
+        depth is not limited.
     :param types: specifies dataset types to include into the listing.
-        Currently allowed keys are "filesystem", "volume", "snapshot".
-        ``None`` is equivalent to specifying the type of the dataset
-        named by `name`.
+        Currently allowed keys are "filesystem", "volume", "snapshot". ``None``
+        is equivalent to specifying the type of the dataset named by `name`.
     :type types: list of bytes or None
     :type recurse: integer or None
-    :return: a list of dictionaries each describing a single listed
-             element.
+    :return: a list of dictionaries each describing a single listed element.
     :rtype: list of dict
     '''
     options = {}
@@ -1126,8 +1808,8 @@ def _list(name, recurse=None, types=None):
             with nvlist_out(result) as nvp:
                 ret = _lib.nvlist_unpack(data_bytes, size, nvp, 0)
             if ret != 0:
-                raise exceptions.ZFSGenericError(ret, None,
-                                                 "Failed to unpack list data")
+                raise exceptions.ZFSGenericError(
+                    ret, None, "Failed to unpack list data")
             yield result
     finally:
         os.close(other_fd)
@@ -1147,8 +1829,8 @@ def lzc_get_props(name):
     :rtype: dict of bytes:Any
 
     .. note::
-        The value of ``clones`` property is a `list` of clone names
-        as byte strings.
+        The value of ``clones`` property is a `list` of clone names as byte
+        strings.
 
     .. warning::
         The returned dictionary does not contain entries for properties
@@ -1174,15 +1856,16 @@ def lzc_get_props(name):
         # is equivalent to the property being set on the current dataset.
         # Note that a normal mountpoint value should start with '/'
         # unlike the special values "none" and "legacy".
-        if mountpoint_val.startswith('/') and not mountpoint_src.startswith('$'):
+        if (mountpoint_val.startswith('/') and
+                not mountpoint_src.startswith('$')):
             mountpoint_val = mountpoint_val + name[len(mountpoint_src):]
     elif not is_snapshot:
         mountpoint_val = '/' + name
     else:
         mountpoint_val = None
-    result = {k: v['value'] for k, v in result.iteritems()}
+    result = {k: result[k]['value'] for k in result}
     if 'clones' in result:
-        result['clones'] = result['clones'].keys()
+        result['clones'] = list(result['clones'].keys())
     if mountpoint_val is not None:
         result['mountpoint'] = mountpoint_val
     return result
@@ -1263,6 +1946,7 @@ def _initialize():
 
     return LazyInit(libzfs_core.lib)
 
+
 _ffi = libzfs_core.ffi
 _lib = _initialize()