xref: /freebsd/sys/contrib/openzfs/contrib/pyzfs/libzfs_core/_libzfs_core.py (revision 61145dc2b94f12f6a47344fb9aac702321880e43)
1# SPDX-License-Identifier: Apache-2.0
2#
3# Copyright 2015 ClusterHQ
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#    http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18"""
19Python wrappers for libzfs_core interfaces.
20
21As a rule, there is a Python function for each C function.
22The signatures of the Python functions generally follow those of the
23functions, but the argument types are natural to Python.
24nvlists are wrapped as dictionaries or lists depending on their usage.
25Some parameters have default values depending on typical use for
26increased convenience.  Output parameters are not used and return values
27are directly returned.  Error conditions are signalled by exceptions
28rather than by integer error codes.
29"""
30from __future__ import absolute_import, division, print_function
31
32import errno
33import functools
34import fcntl
35import os
36import struct
37import threading
38from . import exceptions
39from . import _error_translation as errors
40from .bindings import libzfs_core
41from ._constants import (  # noqa: F401
42    MAXNAMELEN,
43    ZCP_DEFAULT_INSTRLIMIT,
44    ZCP_DEFAULT_MEMLIMIT,
45    WRAPPING_KEY_LEN,
46    zfs_key_location,
47    zfs_keyformat,
48    zio_encrypt
49)
50from .ctypes import (
51    int32_t,
52    uint64_t
53)
54from ._nvlist import nvlist_in, nvlist_out
55
56
57def _uncommitted(depends_on=None):
58    '''
59    Mark an API function as being an uncommitted extension that might not be
60    available.
61
62    :param function depends_on: the function that would be checked instead of
63        a decorated function. For example, if the decorated function uses
64        another uncommitted function.
65
66    This decorator transforms a decorated function to raise
67    :exc:`NotImplementedError` if the C libzfs_core library does not provide
68    a function with the same name as the decorated function.
69
70    The optional `depends_on` parameter can be provided if the decorated
71    function does not directly call the C function but instead calls another
72    Python function that follows the typical convention.
73    One example is :func:`lzc_list_snaps` that calls :func:`lzc_list` that
74    calls ``lzc_list`` in libzfs_core.
75
76    This decorator is implemented using :func:`is_supported`.
77    '''
78    def _uncommitted_decorator(func, depends_on=depends_on):
79        @functools.wraps(func)
80        def _f(*args, **kwargs):
81            if not is_supported(_f):
82                raise NotImplementedError(func.__name__)
83            return func(*args, **kwargs)
84        if depends_on is not None:
85            _f._check_func = depends_on
86        return _f
87    return _uncommitted_decorator
88
89
90def lzc_create(name, ds_type='zfs', props=None, key=None):
91    '''
92    Create a ZFS filesystem or a ZFS volume ("zvol").
93
94    :param bytes name: a name of the dataset to be created.
95    :param str ds_type: the type of the dataset to be created,
96        currently supported types are "zfs" (the default) for a filesystem and
97        "zvol" for a volume.
98    :param props: a `dict` of ZFS dataset property name-value pairs
99        (empty by default).
100    :type props: dict of bytes:Any
101    :param key: dataset encryption key data (empty by default).
102    :type key: bytes
103
104    :raises FilesystemExists: if a dataset with the given name already exists.
105    :raises ParentNotFound: if a parent dataset of the requested dataset does
106        not exist.
107    :raises PropertyInvalid: if one or more of the specified properties is
108        invalid or has an invalid type or value.
109    :raises NameInvalid: if the name is not a valid dataset name.
110    :raises NameTooLong: if the name is too long.
111    :raises WrongParent: if the parent dataset of the requested dataset is not
112        a filesystem (e.g. ZVOL)
113    '''
114    if props is None:
115        props = {}
116    if key is None:
117        key = b""
118    else:
119        key = bytes(key)
120    if ds_type == 'zfs':
121        ds_type = _lib.DMU_OST_ZFS
122    elif ds_type == 'zvol':
123        ds_type = _lib.DMU_OST_ZVOL
124    else:
125        raise exceptions.DatasetTypeInvalid(ds_type)
126    nvlist = nvlist_in(props)
127    ret = _lib.lzc_create(name, ds_type, nvlist, key, len(key))
128    errors.lzc_create_translate_error(ret, name, ds_type, props)
129
130
131def lzc_clone(name, origin, props=None):
132    '''
133    Clone a ZFS filesystem or a ZFS volume ("zvol") from a given snapshot.
134
135    :param bytes name: a name of the dataset to be created.
136    :param bytes origin: a name of the origin snapshot.
137    :param props: a `dict` of ZFS dataset property name-value pairs
138        (empty by default).
139    :type props: dict of bytes:Any
140
141    :raises FilesystemExists: if a dataset with the given name already exists.
142    :raises DatasetNotFound: if either a parent dataset of the requested
143        dataset or the origin snapshot does not exist.
144    :raises PropertyInvalid: if one or more of the specified properties is
145        invalid or has an invalid type or value.
146    :raises FilesystemNameInvalid: if the name is not a valid dataset name.
147    :raises SnapshotNameInvalid: if the origin is not a valid snapshot name.
148    :raises NameTooLong: if the name or the origin name is too long.
149    :raises PoolsDiffer: if the clone and the origin have different pool names.
150
151    .. note::
152        Because of a deficiency of the underlying C interface
153        :exc:`.DatasetNotFound` can mean that either a parent filesystem of
154        the target or the origin snapshot does not exist.
155        It is currently impossible to distinguish between the cases.
156        :func:`lzc_hold` can be used to check that the snapshot exists and
157        ensure that it is not destroyed before cloning.
158    '''
159    if props is None:
160        props = {}
161    nvlist = nvlist_in(props)
162    ret = _lib.lzc_clone(name, origin, nvlist)
163    errors.lzc_clone_translate_error(ret, name, origin, props)
164
165
166def lzc_rollback(name):
167    '''
168    Roll back a filesystem or volume to its most recent snapshot.
169
170    Note that the latest snapshot may change if a new one is concurrently
171    created or the current one is destroyed.  lzc_rollback_to can be used
172    to roll back to a specific latest snapshot.
173
174    :param bytes name: a name of the dataset to be rolled back.
175    :return: a name of the most recent snapshot.
176    :rtype: bytes
177
178    :raises FilesystemNotFound: if the dataset does not exist.
179    :raises SnapshotNotFound: if the dataset does not have any snapshots.
180    :raises NameInvalid: if the dataset name is invalid.
181    :raises NameTooLong: if the dataset name is too long.
182    '''
183    # Account for terminating NUL in C strings.
184    snapnamep = _ffi.new('char[]', MAXNAMELEN + 1)
185    ret = _lib.lzc_rollback(name, snapnamep, MAXNAMELEN + 1)
186    errors.lzc_rollback_translate_error(ret, name)
187    return _ffi.string(snapnamep)
188
189
190def lzc_rollback_to(name, snap):
191    '''
192    Roll back this filesystem or volume to the specified snapshot, if possible.
193
194    :param bytes name: a name of the dataset to be rolled back.
195    :param bytes snap: a name of the snapshot to be rolled back.
196
197    :raises FilesystemNotFound: if the dataset does not exist.
198    :raises SnapshotNotFound: if the dataset does not have any snapshots.
199    :raises NameInvalid: if the dataset name is invalid.
200    :raises NameTooLong: if the dataset name is too long.
201    :raises SnapshotNotLatest: if the snapshot is not the latest.
202    '''
203    ret = _lib.lzc_rollback_to(name, snap)
204    errors.lzc_rollback_to_translate_error(ret, name, snap)
205
206
207def lzc_snapshot(snaps, props=None):
208    '''
209    Create snapshots.
210
211    All snapshots must be in the same pool.
212
213    Optionally snapshot properties can be set on all snapshots.
214    Currently  only user properties (prefixed with "user:") are supported.
215
216    Either all snapshots are successfully created or none are created if
217    an exception is raised.
218
219    :param snaps: a list of names of snapshots to be created.
220    :type snaps: list of bytes
221    :param props: a `dict` of ZFS dataset property name-value pairs
222        (empty by default).
223    :type props: dict of bytes:bytes
224
225    :raises SnapshotFailure: if one or more snapshots could not be created.
226
227    .. note::
228        :exc:`.SnapshotFailure` is a compound exception that provides at least
229        one detailed error object in :attr:`SnapshotFailure.errors` `list`.
230
231    .. warning::
232        The underlying implementation reports an individual, per-snapshot error
233        only for :exc:`.SnapshotExists` condition and *sometimes* for
234        :exc:`.NameTooLong`.
235        In all other cases a single error is reported without connection to any
236        specific snapshot name(s).
237
238        This has the following implications:
239
240        * if multiple error conditions are encountered only one of them is
241          reported
242
243        * unless only one snapshot is requested then it is impossible to tell
244          how many snapshots are problematic and what they are
245
246        * only if there are no other error conditions :exc:`.SnapshotExists`
247          is reported for all affected snapshots
248
249        * :exc:`.NameTooLong` can behave either in the same way as
250          :exc:`.SnapshotExists` or as all other exceptions.
251          The former is the case where the full snapshot name exceeds the
252          maximum allowed length but the short snapshot name (after '@') is
253          within the limit.
254          The latter is the case when the short name alone exceeds the maximum
255          allowed length.
256    '''
257    snaps_dict = {name: None for name in snaps}
258    errlist = {}
259    snaps_nvlist = nvlist_in(snaps_dict)
260    if props is None:
261        props = {}
262    props_nvlist = nvlist_in(props)
263    with nvlist_out(errlist) as errlist_nvlist:
264        ret = _lib.lzc_snapshot(snaps_nvlist, props_nvlist, errlist_nvlist)
265    errors.lzc_snapshot_translate_errors(ret, errlist, snaps, props)
266
267
268lzc_snap = lzc_snapshot
269
270
271def lzc_destroy_snaps(snaps, defer):
272    '''
273    Destroy snapshots.
274
275    They must all be in the same pool.
276    Snapshots that do not exist will be silently ignored.
277
278    If 'defer' is not set, and a snapshot has user holds or clones, the
279    destroy operation will fail and none of the snapshots will be
280    destroyed.
281
282    If 'defer' is set, and a snapshot has user holds or clones, it will be
283    marked for deferred destruction, and will be destroyed when the last hold
284    or clone is removed/destroyed.
285
286    The operation succeeds if all snapshots were destroyed (or marked for
287    later destruction if 'defer' is set) or didn't exist to begin with.
288
289    :param snaps: a list of names of snapshots to be destroyed.
290    :type snaps: list of bytes
291    :param bool defer: whether to mark busy snapshots for deferred destruction
292        rather than immediately failing.
293
294    :raises SnapshotDestructionFailure: if one or more snapshots could not be
295        created.
296
297    .. note::
298        :exc:`.SnapshotDestructionFailure` is a compound exception that
299        provides at least one detailed error object in
300        :attr:`SnapshotDestructionFailure.errors` `list`.
301
302        Typical error is :exc:`SnapshotIsCloned` if `defer` is `False`.
303        The snapshot names are validated quite loosely and invalid names are
304        typically ignored as nonexisting snapshots.
305
306        A snapshot name referring to a filesystem that doesn't exist is
307        ignored.
308        However, non-existent pool name causes :exc:`PoolNotFound`.
309    '''
310    snaps_dict = {name: None for name in snaps}
311    errlist = {}
312    snaps_nvlist = nvlist_in(snaps_dict)
313    with nvlist_out(errlist) as errlist_nvlist:
314        ret = _lib.lzc_destroy_snaps(snaps_nvlist, defer, errlist_nvlist)
315    errors.lzc_destroy_snaps_translate_errors(ret, errlist, snaps, defer)
316
317
318def lzc_bookmark(bookmarks):
319    '''
320    Create bookmarks.
321
322    :param bookmarks: a dict that maps names of wanted bookmarks to names of
323        existing snapshots or bookmarks.
324    :type bookmarks: dict of bytes to bytes
325    :raises BookmarkFailure: if any of the bookmarks can not be created for any
326        reason.
327
328    The bookmarks `dict` maps from name of the bookmark
329    (e.g. :file:`{pool}/{fs}#{bmark}`) to the name of the snapshot
330    (e.g. :file:`{pool}/{fs}@{snap}`) or existint bookmark
331    :file:`{pool}/{fs}@{snap}`. All the bookmarks and snapshots must
332    be in the same pool.
333    '''
334    errlist = {}
335    nvlist = nvlist_in(bookmarks)
336    with nvlist_out(errlist) as errlist_nvlist:
337        ret = _lib.lzc_bookmark(nvlist, errlist_nvlist)
338    errors.lzc_bookmark_translate_errors(ret, errlist, bookmarks)
339
340
341def lzc_get_bookmarks(fsname, props=None):
342    '''
343    Retrieve a listing of bookmarks for the given file system.
344
345    :param bytes fsname: a name of the filesystem.
346    :param props: a `list` of properties that will be returned for each
347        bookmark.
348    :type props: list of bytes
349    :return: a `dict` that maps the bookmarks' short names to their properties.
350    :rtype: dict of bytes:dict
351
352    :raises FilesystemNotFound: if the filesystem is not found.
353
354    The following are valid properties on bookmarks:
355
356    guid : integer
357        globally unique identifier of the snapshot the bookmark refers to
358    createtxg : integer
359        txg when the snapshot the bookmark refers to was created
360    creation : integer
361        timestamp when the snapshot the bookmark refers to was created
362
363    Any other properties passed in ``props`` are ignored without reporting
364    any error.
365    Values in the returned dictionary map the names of the requested properties
366    to their respective values.
367    '''
368    bmarks = {}
369    if props is None:
370        props = []
371    props_dict = {name: None for name in props}
372    nvlist = nvlist_in(props_dict)
373    with nvlist_out(bmarks) as bmarks_nvlist:
374        ret = _lib.lzc_get_bookmarks(fsname, nvlist, bmarks_nvlist)
375    errors.lzc_get_bookmarks_translate_error(ret, fsname, props)
376    return bmarks
377
378
379def lzc_destroy_bookmarks(bookmarks):
380    '''
381    Destroy bookmarks.
382
383    :param bookmarks: a list of the bookmarks to be destroyed. The bookmarks
384        are specified as :file:`{fs}#{bmark}`.
385    :type bookmarks: list of bytes
386
387    :raises BookmarkDestructionFailure: if any of the bookmarks may not be
388        destroyed.
389
390    The bookmarks must all be in the same pool.
391    Bookmarks that do not exist will be silently ignored.
392    This also includes the case where the filesystem component of the bookmark
393    name does not exist.
394    However, an invalid bookmark name will cause :exc:`.NameInvalid` error
395    reported in :attr:`SnapshotDestructionFailure.errors`.
396
397    Either all bookmarks that existed are destroyed or an exception is raised.
398    '''
399    errlist = {}
400    bmarks_dict = {name: None for name in bookmarks}
401    nvlist = nvlist_in(bmarks_dict)
402    with nvlist_out(errlist) as errlist_nvlist:
403        ret = _lib.lzc_destroy_bookmarks(nvlist, errlist_nvlist)
404    errors.lzc_destroy_bookmarks_translate_errors(ret, errlist, bookmarks)
405
406
407def lzc_snaprange_space(firstsnap, lastsnap):
408    '''
409    Calculate a size of data referenced by snapshots in the inclusive range
410    between the ``firstsnap`` and the ``lastsnap`` and not shared with any
411    other datasets.
412
413    :param bytes firstsnap: the name of the first snapshot in the range.
414    :param bytes lastsnap: the name of the last snapshot in the range.
415    :return: the calculated stream size, in bytes.
416    :rtype: `int` or `long`
417
418    :raises SnapshotNotFound: if either of the snapshots does not exist.
419    :raises NameInvalid: if the name of either snapshot is invalid.
420    :raises NameTooLong: if the name of either snapshot is too long.
421    :raises SnapshotMismatch: if ``fromsnap`` is not an ancestor snapshot of
422        ``snapname``.
423    :raises PoolsDiffer: if the snapshots belong to different pools.
424
425    ``lzc_snaprange_space`` calculates total size of blocks that exist
426    because they are referenced only by one or more snapshots in the given
427    range but no other dataset.
428    In other words, this is the set of blocks that were born after the snap
429    before firstsnap, and died before the snap after the last snap.
430    Yet another interpretation is that the result of ``lzc_snaprange_space``
431    is the size of the space that would be freed if the snapshots in the range
432    are destroyed.
433
434    If the same snapshot is given as both the ``firstsnap`` and the
435    ``lastsnap``.
436    In that case ``lzc_snaprange_space`` calculates space used by the snapshot.
437    '''
438    valp = _ffi.new('uint64_t *')
439    ret = _lib.lzc_snaprange_space(firstsnap, lastsnap, valp)
440    errors.lzc_snaprange_space_translate_error(ret, firstsnap, lastsnap)
441    return int(valp[0])
442
443
444def lzc_hold(holds, fd=None):
445    '''
446    Create *user holds* on snapshots.  If there is a hold on a snapshot,
447    the snapshot can not be destroyed.  (However, it can be marked for
448    deletion by :func:`lzc_destroy_snaps` ( ``defer`` = `True` ).)
449
450    :param holds: the dictionary of names of the snapshots to hold mapped to
451        the hold names.
452    :type holds: dict of bytes : bytes
453    :type fd: int or None
454    :param fd: if not None then it must be the result of :func:`os.open`
455        called as ``os.open("/dev/zfs", O_EXCL)``.
456    :type fd: int or None
457    :return: a list of the snapshots that do not exist.
458    :rtype: list of bytes
459
460    :raises HoldFailure: if a hold was impossible on one or more of the
461        snapshots.
462    :raises BadHoldCleanupFD: if ``fd`` is not a valid file descriptor
463        associated with :file:`/dev/zfs`.
464
465    The snapshots must all be in the same pool.
466
467    If ``fd`` is not None, then when the ``fd`` is closed (including on process
468    termination), the holds will be released.  If the system is shut down
469    uncleanly, the holds will be released when the pool is next opened
470    or imported.
471
472    Holds for snapshots which don't exist will be skipped and have an entry
473    added to the return value, but will not cause an overall failure.
474    No exceptions is raised if all holds, for snapshots that existed, were
475    successfully created.
476    Otherwise :exc:`.HoldFailure` exception is raised and no holds will be
477    created.
478    :attr:`.HoldFailure.errors` may contain a single element for an error that
479    is not specific to any hold / snapshot, or it may contain one or more
480    elements detailing specific error per each affected hold.
481    '''
482    errlist = {}
483    if fd is None:
484        fd = -1
485    nvlist = nvlist_in(holds)
486    with nvlist_out(errlist) as errlist_nvlist:
487        ret = _lib.lzc_hold(nvlist, fd, errlist_nvlist)
488    errors.lzc_hold_translate_errors(ret, errlist, holds, fd)
489    # If there is no error (no exception raised by _handleErrList), but errlist
490    # is not empty, then it contains missing snapshots.
491    assert all(errlist[x] == errno.ENOENT for x in errlist)
492    return list(errlist.keys())
493
494
495def lzc_release(holds):
496    '''
497    Release *user holds* on snapshots.
498
499    If the snapshot has been marked for
500    deferred destroy (by lzc_destroy_snaps(defer=B_TRUE)), it does not have
501    any clones, and all the user holds are removed, then the snapshot will be
502    destroyed.
503
504    The snapshots must all be in the same pool.
505
506    :param holds: a ``dict`` where keys are snapshot names and values are
507        lists of hold tags to remove.
508    :type holds: dict of bytes : list of bytes
509    :return: a list of any snapshots that do not exist and of any tags that do
510        not exist for existing snapshots.
511        Such tags are qualified with a corresponding snapshot name using the
512        following format :file:`{pool}/{fs}@{snap}#{tag}`
513    :rtype: list of bytes
514
515    :raises HoldReleaseFailure: if one or more existing holds could not be
516        released.
517
518    Holds which failed to release because they didn't exist will have an entry
519    added to errlist, but will not cause an overall failure.
520
521    This call is success if ``holds`` was empty or all holds that
522    existed, were successfully removed.
523    Otherwise an exception will be raised.
524    '''
525    errlist = {}
526    holds_dict = {}
527    for snap in holds:
528        hold_list = holds[snap]
529        if not isinstance(hold_list, list):
530            raise TypeError('holds must be in a list')
531        holds_dict[snap] = {hold: None for hold in hold_list}
532    nvlist = nvlist_in(holds_dict)
533    with nvlist_out(errlist) as errlist_nvlist:
534        ret = _lib.lzc_release(nvlist, errlist_nvlist)
535    errors.lzc_release_translate_errors(ret, errlist, holds)
536    # If there is no error (no exception raised by _handleErrList), but errlist
537    # is not empty, then it contains missing snapshots and tags.
538    assert all(errlist[x] == errno.ENOENT for x in errlist)
539    return list(errlist.keys())
540
541
542def lzc_get_holds(snapname):
543    '''
544    Retrieve list of *user holds* on the specified snapshot.
545
546    :param bytes snapname: the name of the snapshot.
547    :return: holds on the snapshot along with their creation times
548        in seconds since the epoch
549    :rtype: dict of bytes : int
550    '''
551    holds = {}
552    with nvlist_out(holds) as nvlist:
553        ret = _lib.lzc_get_holds(snapname, nvlist)
554    errors.lzc_get_holds_translate_error(ret, snapname)
555    return holds
556
557
558def lzc_send(snapname, fromsnap, fd, flags=None):
559    '''
560    Generate a zfs send stream for the specified snapshot and write it to
561    the specified file descriptor.
562
563    :param bytes snapname: the name of the snapshot to send.
564    :param fromsnap: if not None the name of the starting snapshot
565        for the incremental stream.
566    :type fromsnap: bytes or None
567    :param int fd: the file descriptor to write the send stream to.
568    :param flags: the flags that control what enhanced features can be used in
569        the stream.
570    :type flags: list of bytes
571
572    :raises SnapshotNotFound: if either the starting snapshot is not `None` and
573        does not exist, or if the ending snapshot does not exist.
574    :raises NameInvalid: if the name of either snapshot is invalid.
575    :raises NameTooLong: if the name of either snapshot is too long.
576    :raises SnapshotMismatch: if ``fromsnap`` is not an ancestor snapshot of
577        ``snapname``.
578    :raises PoolsDiffer: if the snapshots belong to different pools.
579    :raises IOError: if an input / output error occurs while writing to ``fd``.
580    :raises UnknownStreamFeature: if the ``flags`` contain an unknown flag
581        name.
582
583    If ``fromsnap`` is None, a full (non-incremental) stream will be sent.
584    If ``fromsnap`` is not None, it must be the full name of a snapshot or
585    bookmark to send an incremental from, e.g.
586    :file:`{pool}/{fs}@{earlier_snap}` or :file:`{pool}/{fs}#{earlier_bmark}`.
587
588    The specified snapshot or bookmark must represent an earlier point in the
589    history of ``snapname``.
590    It can be an earlier snapshot in the same filesystem or zvol as
591    ``snapname``, or it can be the origin of ``snapname``'s filesystem, or an
592    earlier snapshot in the origin, etc.
593    ``fromsnap`` must be strictly an earlier snapshot, specifying the same
594    snapshot as both ``fromsnap`` and ``snapname`` is an error.
595
596    If ``flags`` contains *"large_blocks"*, the stream is permitted
597    to contain ``DRR_WRITE`` records with ``drr_length`` > 128K,
598    and ``DRR_OBJECT`` records with ``drr_blksz`` > 128K.
599
600    If ``flags`` contains *"embedded_data"*, the stream is permitted
601    to contain ``DRR_WRITE_EMBEDDED`` records with
602    ``drr_etype`` == ``BP_EMBEDDED_TYPE_DATA``,
603    which the receiving system must support (as indicated by support
604    for the *embedded_data* feature).
605
606    If ``flags`` contains *"compress"*, the stream is generated by using
607    compressed WRITE records for blocks which are compressed on disk and
608    in memory.  If the *lz4_compress* feature is active on the sending
609    system, then the receiving system must have that feature enabled as well.
610
611    If ``flags`` contains *"raw"*, the stream is generated, for encrypted
612    datasets, by sending data exactly as it exists on disk.  This allows
613    backups to be taken even if encryption keys are not currently loaded.
614
615    .. note::
616        ``lzc_send`` can actually accept a filesystem name as the ``snapname``.
617        In that case ``lzc_send`` acts as if a temporary snapshot was created
618        after the start of the call and before the stream starts being
619        produced.
620
621    .. note::
622        ``lzc_send`` does not return until all of the stream is written to
623        ``fd``.
624
625    .. note::
626        ``lzc_send`` does *not* close ``fd`` upon returning.
627    '''
628    if fromsnap is not None:
629        c_fromsnap = fromsnap
630    else:
631        c_fromsnap = _ffi.NULL
632    c_flags = 0
633    if flags is None:
634        flags = []
635    for flag in flags:
636        c_flag = {
637            'embedded_data': _lib.LZC_SEND_FLAG_EMBED_DATA,
638            'large_blocks': _lib.LZC_SEND_FLAG_LARGE_BLOCK,
639            'compress': _lib.LZC_SEND_FLAG_COMPRESS,
640            'raw': _lib.LZC_SEND_FLAG_RAW,
641        }.get(flag)
642        if c_flag is None:
643            raise exceptions.UnknownStreamFeature(flag)
644        c_flags |= c_flag
645
646    ret = _lib.lzc_send(snapname, c_fromsnap, fd, c_flags)
647    errors.lzc_send_translate_error(ret, snapname, fromsnap, fd, flags)
648
649
650def lzc_send_space(snapname, fromsnap=None, flags=None):
651    '''
652    Estimate size of a full or incremental backup stream
653    given the optional starting snapshot and the ending snapshot.
654
655    :param bytes snapname: the name of the snapshot for which the estimate
656        should be done.
657    :param fromsnap: the optional starting snapshot name.
658        If not `None` then an incremental stream size is estimated, otherwise
659        a full stream is estimated.
660    :type fromsnap: `bytes` or `None`
661    :param flags: the flags that control what enhanced features can be used
662        in the stream.
663    :type flags: list of bytes
664
665    :return: the estimated stream size, in bytes.
666    :rtype: `int` or `long`
667
668    :raises SnapshotNotFound: if either the starting snapshot is not `None` and
669        does not exist, or if the ending snapshot does not exist.
670    :raises NameInvalid: if the name of either snapshot is invalid.
671    :raises NameTooLong: if the name of either snapshot is too long.
672    :raises SnapshotMismatch: if ``fromsnap`` is not an ancestor snapshot of
673        ``snapname``.
674    :raises PoolsDiffer: if the snapshots belong to different pools.
675
676    ``fromsnap``, if not ``None``,  must be strictly an earlier snapshot,
677    specifying the same snapshot as both ``fromsnap`` and ``snapname`` is an
678    error.
679    '''
680    if fromsnap is not None:
681        c_fromsnap = fromsnap
682    else:
683        c_fromsnap = _ffi.NULL
684    c_flags = 0
685    if flags is None:
686        flags = []
687    for flag in flags:
688        c_flag = {
689            'embedded_data': _lib.LZC_SEND_FLAG_EMBED_DATA,
690            'large_blocks': _lib.LZC_SEND_FLAG_LARGE_BLOCK,
691            'compress': _lib.LZC_SEND_FLAG_COMPRESS,
692            'raw': _lib.LZC_SEND_FLAG_RAW,
693        }.get(flag)
694        if c_flag is None:
695            raise exceptions.UnknownStreamFeature(flag)
696        c_flags |= c_flag
697    valp = _ffi.new('uint64_t *')
698
699    ret = _lib.lzc_send_space(snapname, c_fromsnap, c_flags, valp)
700    errors.lzc_send_space_translate_error(ret, snapname, fromsnap)
701    return int(valp[0])
702
703
704def lzc_receive(snapname, fd, force=False, raw=False, origin=None, props=None):
705    '''
706    Receive from the specified ``fd``, creating the specified snapshot.
707
708    :param bytes snapname: the name of the snapshot to create.
709    :param int fd: the file descriptor from which to read the stream.
710    :param bool force: whether to roll back or destroy the target filesystem
711        if that is required to receive the stream.
712    :param bool raw: whether this is a "raw" stream.
713    :param origin: the optional origin snapshot name if the stream is for a
714        clone.
715    :type origin: bytes or None
716    :param props: the properties to set on the snapshot as *received*
717        properties.
718    :type props: dict of bytes : Any
719
720    :raises IOError: if an input / output error occurs while reading from the
721        ``fd``.
722    :raises DatasetExists: if the snapshot named ``snapname`` already exists.
723    :raises DatasetExists: if the stream is a full stream and the destination
724        filesystem already exists.
725    :raises DatasetExists: if ``force`` is `True` but the destination
726        filesystem could not be rolled back to a matching snapshot because a
727        newer snapshot exists and it is an origin of a cloned filesystem.
728    :raises StreamMismatch: if an incremental stream is received and the latest
729        snapshot of the destination filesystem does not match the source
730        snapshot of the stream.
731    :raises StreamMismatch: if a full stream is received and the destination
732        filesystem already exists and it has at least one snapshot, and
733        ``force`` is `False`.
734    :raises StreamMismatch: if an incremental clone stream is received but the
735        specified ``origin`` is not the actual received origin.
736    :raises DestinationModified: if an incremental stream is received and the
737        destination filesystem has been modified since the last snapshot and
738        ``force`` is `False`.
739    :raises DestinationModified: if a full stream is received and the
740        destination filesystem already exists and it does not have any
741        snapshots, and ``force`` is `False`.
742    :raises DatasetNotFound: if the destination filesystem and its parent do
743        not exist.
744    :raises DatasetNotFound: if the ``origin`` is not `None` and does not
745        exist.
746    :raises DatasetBusy: if ``force`` is `True` but the destination filesystem
747        could not be rolled back to a matching snapshot because a newer
748        snapshot is held and could not be destroyed.
749    :raises DatasetBusy: if another receive operation is being performed on the
750        destination filesystem.
751    :raises BadStream: if the stream is corrupt or it is not recognized or it
752        is a compound stream or it is a clone stream, but ``origin`` is `None`.
753    :raises BadStream: if a clone stream is received and the destination
754        filesystem already exists.
755    :raises StreamFeatureNotSupported: if the stream has a feature that is not
756        supported on this side.
757    :raises NameInvalid: if the name of either snapshot is invalid.
758    :raises NameTooLong: if the name of either snapshot is too long.
759    :raises WrongParent: if the parent dataset of the received destination is
760        not a filesystem (e.g. ZVOL)
761
762    .. note::
763        The ``origin`` is ignored if the actual stream is an incremental stream
764        that is not a clone stream and the destination filesystem exists.
765        If the stream is a full stream and the destination filesystem does not
766        exist then the ``origin`` is checked for existence: if it does not
767        exist :exc:`.DatasetNotFound` is raised, otherwise
768        :exc:`.StreamMismatch` is raised, because that snapshot can not have
769        any relation to the stream.
770
771    .. note::
772        If ``force`` is `True` and the stream is incremental then the
773        destination filesystem is rolled back to a matching source snapshot if
774        necessary. Intermediate snapshots are destroyed in that case.
775
776        However, none of the existing snapshots may have the same name as
777        ``snapname`` even if such a snapshot were to be destroyed.
778        The existing ``snapname`` snapshot always causes
779        :exc:`.SnapshotExists` to be raised.
780
781        If ``force`` is `True` and the stream is a full stream then the
782        destination filesystem is replaced with the received filesystem unless
783        the former has any snapshots.  This prevents the destination filesystem
784        from being rolled back / replaced.
785
786    .. note::
787        This interface does not work on dedup'd streams
788        (those with ``DMU_BACKUP_FEATURE_DEDUP``).
789
790    .. note::
791        ``lzc_receive`` does not return until all of the stream is read from
792        ``fd`` and applied to the pool.
793
794    .. note::
795        ``lzc_receive`` does *not* close ``fd`` upon returning.
796    '''
797
798    if origin is not None:
799        c_origin = origin
800    else:
801        c_origin = _ffi.NULL
802    if props is None:
803        props = {}
804    nvlist = nvlist_in(props)
805    ret = _lib.lzc_receive(snapname, nvlist, c_origin, force, raw, fd)
806    errors.lzc_receive_translate_errors(
807        ret, snapname, fd, force, raw, False, False, origin, None
808    )
809
810
811lzc_recv = lzc_receive
812
813
814def lzc_exists(name):
815    '''
816    Check if a dataset (a filesystem, or a volume, or a snapshot)
817    with the given name exists.
818
819    :param bytes name: the dataset name to check.
820    :return: `True` if the dataset exists, `False` otherwise.
821    :rtype: bool
822
823    .. note::
824        ``lzc_exists`` can not be used to check for existence of bookmarks.
825    '''
826    ret = _lib.lzc_exists(name)
827    return bool(ret)
828
829
830@_uncommitted()
831def lzc_change_key(fsname, crypt_cmd, props=None, key=None):
832    '''
833    Change encryption key on the specified dataset.
834
835    :param bytes fsname: the name of the dataset.
836    :param str crypt_cmd: the encryption "command" to be executed, currently
837        supported values are "new_key", "inherit", "force_new_key" and
838        "force_inherit".
839    :param props: a `dict` of encryption-related property name-value pairs;
840        only "keyformat", "keylocation" and "pbkdf2iters" are supported
841        (empty by default).
842    :type props: dict of bytes:Any
843    :param key: dataset encryption key data (empty by default).
844    :type key: bytes
845
846    :raises PropertyInvalid: if ``props`` contains invalid values.
847    :raises FilesystemNotFound: if the dataset does not exist.
848    :raises UnknownCryptCommand: if ``crypt_cmd`` is invalid.
849    :raises EncryptionKeyNotLoaded: if the encryption key is not currently
850        loaded and therefore cannot be changed.
851    '''
852    if props is None:
853        props = {}
854    if key is None:
855        key = b""
856    else:
857        key = bytes(key)
858    cmd = {
859        'new_key': _lib.DCP_CMD_NEW_KEY,
860        'inherit': _lib.DCP_CMD_INHERIT,
861        'force_new_key': _lib.DCP_CMD_FORCE_NEW_KEY,
862        'force_inherit': _lib.DCP_CMD_FORCE_INHERIT,
863    }.get(crypt_cmd)
864    if cmd is None:
865        raise exceptions.UnknownCryptCommand(crypt_cmd)
866    nvlist = nvlist_in(props)
867    ret = _lib.lzc_change_key(fsname, cmd, nvlist, key, len(key))
868    errors.lzc_change_key_translate_error(ret, fsname)
869
870
871@_uncommitted()
872def lzc_load_key(fsname, noop, key):
873    '''
874    Load or verify encryption key on the specified dataset.
875
876    :param bytes fsname: the name of the dataset.
877    :param bool noop: if `True` the encryption key will only be verified,
878        not loaded.
879    :param key: dataset encryption key data.
880    :type key: bytes
881
882    :raises FilesystemNotFound: if the dataset does not exist.
883    :raises EncryptionKeyAlreadyLoaded: if the encryption key is already
884        loaded.
885    :raises EncryptionKeyInvalid: if the encryption key provided is incorrect.
886    '''
887    ret = _lib.lzc_load_key(fsname, noop, key, len(key))
888    errors.lzc_load_key_translate_error(ret, fsname, noop)
889
890
891@_uncommitted()
892def lzc_unload_key(fsname):
893    '''
894    Unload encryption key from the specified dataset.
895
896    :param bytes fsname: the name of the dataset.
897
898    :raises FilesystemNotFound: if the dataset does not exist.
899    :raises DatasetBusy: if the encryption key is still being used. This
900        usually occurs when the dataset is mounted.
901    :raises EncryptionKeyNotLoaded: if the encryption key is not currently
902        loaded.
903    '''
904    ret = _lib.lzc_unload_key(fsname)
905    errors.lzc_unload_key_translate_error(ret, fsname)
906
907
908def lzc_channel_program(
909    poolname, program, instrlimit=ZCP_DEFAULT_INSTRLIMIT,
910    memlimit=ZCP_DEFAULT_MEMLIMIT, params=None
911):
912    '''
913    Executes a script as a ZFS channel program on pool ``poolname``.
914
915    :param bytes poolname: the name of the pool.
916    :param bytes program: channel program text.
917    :param int instrlimit: execution time limit, in milliseconds.
918    :param int memlimit: execution memory limit, in bytes.
919    :param bytes params: a `list` of parameters passed to the channel program
920        (empty by default).
921    :type params: dict of bytes:Any
922    :return: a dictionary of result values procuced by the channel program,
923        if any.
924    :rtype: dict
925
926    :raises PoolNotFound: if the pool does not exist.
927    :raises ZCPLimitInvalid: if either instruction or memory limit are invalid.
928    :raises ZCPSyntaxError: if the channel program contains syntax errors.
929    :raises ZCPTimeout: if the channel program took too long to execute.
930    :raises ZCPSpaceError: if the channel program exhausted the memory limit.
931    :raises ZCPMemoryError: if the channel program return value was too large.
932    :raises ZCPPermissionError: if the user lacks the permission to run the
933        channel program. Channel programs must be run as root.
934    :raises ZCPRuntimeError: if the channel program encountered a runtime
935        error.
936    '''
937    output = {}
938    params_nv = nvlist_in({b"argv": params})
939    with nvlist_out(output) as outnvl:
940        ret = _lib.lzc_channel_program(
941            poolname, program, instrlimit, memlimit, params_nv, outnvl)
942    errors.lzc_channel_program_translate_error(
943        ret, poolname, output.get(b"error"))
944    return output.get(b"return")
945
946
947def lzc_channel_program_nosync(
948    poolname, program, instrlimit=ZCP_DEFAULT_INSTRLIMIT,
949    memlimit=ZCP_DEFAULT_MEMLIMIT, params=None
950):
951    '''
952    Executes a script as a read-only ZFS channel program on pool ``poolname``.
953    A read-only channel program works programmatically the same way as a
954    normal channel program executed with
955    :func:`lzc_channel_program`. The only difference is it runs exclusively in
956    open-context and therefore can return faster.
957    The downside to that, is that the program cannot change on-disk state by
958    calling functions from the zfs.sync submodule.
959
960    :param bytes poolname: the name of the pool.
961    :param bytes program: channel program text.
962    :param int instrlimit: execution time limit, in milliseconds.
963    :param int memlimit: execution memory limit, in bytes.
964    :param bytes params: a `list` of parameters passed to the channel program
965        (empty by default).
966    :type params: dict of bytes:Any
967    :return: a dictionary of result values procuced by the channel program,
968        if any.
969    :rtype: dict
970
971    :raises PoolNotFound: if the pool does not exist.
972    :raises ZCPLimitInvalid: if either instruction or memory limit are invalid.
973    :raises ZCPSyntaxError: if the channel program contains syntax errors.
974    :raises ZCPTimeout: if the channel program took too long to execute.
975    :raises ZCPSpaceError: if the channel program exhausted the memory limit.
976    :raises ZCPMemoryError: if the channel program return value was too large.
977    :raises ZCPPermissionError: if the user lacks the permission to run the
978        channel program. Channel programs must be run as root.
979    :raises ZCPRuntimeError: if the channel program encountered a runtime
980        error.
981    '''
982    output = {}
983    params_nv = nvlist_in({b"argv": params})
984    with nvlist_out(output) as outnvl:
985        ret = _lib.lzc_channel_program_nosync(
986            poolname, program, instrlimit, memlimit, params_nv, outnvl)
987    errors.lzc_channel_program_translate_error(
988        ret, poolname, output.get(b"error"))
989    return output.get(b"return")
990
991
992def lzc_receive_resumable(
993    snapname, fd, force=False, raw=False, origin=None, props=None
994):
995    '''
996    Like :func:`lzc_receive`, but if the receive fails due to premature stream
997    termination, the intermediate state will be preserved on disk.  In this
998    case, ECKSUM will be returned.  The receive may subsequently be resumed
999    with a resuming send stream generated by lzc_send_resume().
1000
1001    :param bytes snapname: the name of the snapshot to create.
1002    :param int fd: the file descriptor from which to read the stream.
1003    :param bool force: whether to roll back or destroy the target filesystem
1004        if that is required to receive the stream.
1005    :param bool raw: whether this is a "raw" stream.
1006    :param origin: the optional origin snapshot name if the stream is for a
1007        clone.
1008    :type origin: bytes or None
1009    :param props: the properties to set on the snapshot as *received*
1010        properties.
1011    :type props: dict of bytes : Any
1012
1013    :raises IOError: if an input / output error occurs while reading from the
1014        ``fd``.
1015    :raises DatasetExists: if the snapshot named ``snapname`` already exists.
1016    :raises DatasetExists: if the stream is a full stream and the destination
1017        filesystem already exists.
1018    :raises DatasetExists: if ``force`` is `True` but the destination
1019        filesystem could not be rolled back to a matching snapshot because a
1020        newer snapshot exists and it is an origin of a cloned filesystem.
1021    :raises StreamMismatch: if an incremental stream is received and the latest
1022        snapshot of the destination filesystem does not match the source
1023        snapshot of the stream.
1024    :raises StreamMismatch: if a full stream is received and the destination
1025        filesystem already exists and it has at least one snapshot, and
1026        ``force`` is `False`.
1027    :raises StreamMismatch: if an incremental clone stream is received but the
1028        specified ``origin`` is not the actual received origin.
1029    :raises DestinationModified: if an incremental stream is received and the
1030        destination filesystem has been modified since the last snapshot and
1031        ``force`` is `False`.
1032    :raises DestinationModified: if a full stream is received and the
1033        destination filesystem already exists and it does not have any
1034        snapshots, and ``force`` is `False`.
1035    :raises DatasetNotFound: if the destination filesystem and its parent do
1036        not exist.
1037    :raises DatasetNotFound: if the ``origin`` is not `None` and does not
1038        exist.
1039    :raises DatasetBusy: if ``force`` is `True` but the destination filesystem
1040        could not be rolled back to a matching snapshot because a newer
1041        snapshot is held and could not be destroyed.
1042    :raises DatasetBusy: if another receive operation is being performed on the
1043        destination filesystem.
1044    :raises BadStream: if the stream is corrupt or it is not recognized or it
1045        is a compound stream or it is a clone stream, but ``origin`` is `None`.
1046    :raises BadStream: if a clone stream is received and the destination
1047        filesystem already exists.
1048    :raises StreamFeatureNotSupported: if the stream has a feature that is not
1049        supported on this side.
1050    :raises NameInvalid: if the name of either snapshot is invalid.
1051    :raises NameTooLong: if the name of either snapshot is too long.
1052    '''
1053
1054    if origin is not None:
1055        c_origin = origin
1056    else:
1057        c_origin = _ffi.NULL
1058    if props is None:
1059        props = {}
1060    nvlist = nvlist_in(props)
1061    ret = _lib.lzc_receive_resumable(
1062        snapname, nvlist, c_origin, force, raw, fd)
1063    errors.lzc_receive_translate_errors(
1064        ret, snapname, fd, force, raw, False, False, origin, None)
1065
1066
1067def lzc_receive_with_header(
1068    snapname, fd, begin_record, force=False, resumable=False, raw=False,
1069    origin=None, props=None
1070):
1071    '''
1072    Like :func:`lzc_receive`, but allows the caller to read the begin record
1073    and then to pass it in.
1074
1075    That could be useful if the caller wants to derive, for example,
1076    the snapname or the origin parameters based on the information contained in
1077    the begin record.
1078    :func:`receive_header` can be used to receive the begin record from the
1079    file descriptor.
1080
1081    :param bytes snapname: the name of the snapshot to create.
1082    :param int fd: the file descriptor from which to read the stream.
1083    :param begin_record: the stream's begin record.
1084    :type begin_record: ``cffi`` `CData` representing the dmu_replay_record_t
1085        structure.
1086    :param bool force: whether to roll back or destroy the target filesystem
1087        if that is required to receive the stream.
1088    :param bool resumable: whether this stream should be treated as resumable.
1089        If the receive fails due to premature stream termination, the
1090        intermediate state will be preserved on disk and may subsequently be
1091        resumed with :func:`lzc_send_resume`.
1092    :param bool raw: whether this is a "raw" stream.
1093    :param origin: the optional origin snapshot name if the stream is for a
1094        clone.
1095    :type origin: bytes or None
1096    :param props: the properties to set on the snapshot as *received*
1097        properties.
1098    :type props: dict of bytes : Any
1099
1100    :raises IOError: if an input / output error occurs while reading from the
1101        ``fd``.
1102    :raises DatasetExists: if the snapshot named ``snapname`` already exists.
1103    :raises DatasetExists: if the stream is a full stream and the destination
1104        filesystem already exists.
1105    :raises DatasetExists: if ``force`` is `True` but the destination
1106        filesystem could not be rolled back to a matching snapshot because a
1107        newer snapshot exists and it is an origin of a cloned filesystem.
1108    :raises StreamMismatch: if an incremental stream is received and the latest
1109        snapshot of the destination filesystem does not match the source
1110        snapshot of the stream.
1111    :raises StreamMismatch: if a full stream is received and the destination
1112        filesystem already exists and it has at least one snapshot, and
1113        ``force`` is `False`.
1114    :raises StreamMismatch: if an incremental clone stream is received but the
1115        specified ``origin`` is not the actual received origin.
1116    :raises DestinationModified: if an incremental stream is received and the
1117        destination filesystem has been modified since the last snapshot and
1118        ``force`` is `False`.
1119    :raises DestinationModified: if a full stream is received and the
1120        destination filesystem already exists and it does not have any
1121        snapshots, and ``force`` is `False`.
1122    :raises DatasetNotFound: if the destination filesystem and its parent do
1123        not exist.
1124    :raises DatasetNotFound: if the ``origin`` is not `None` and does not
1125        exist.
1126    :raises DatasetBusy: if ``force`` is `True` but the destination filesystem
1127        could not be rolled back to a matching snapshot because a newer
1128        snapshot is held and could not be destroyed.
1129    :raises DatasetBusy: if another receive operation is being performed on the
1130        destination filesystem.
1131    :raises BadStream: if the stream is corrupt or it is not recognized or it
1132        is a compound stream or it is a clone stream, but ``origin`` is `None`.
1133    :raises BadStream: if a clone stream is received and the destination
1134        filesystem already exists.
1135    :raises StreamFeatureNotSupported: if the stream has a feature that is not
1136        supported on this side.
1137    :raises NameInvalid: if the name of either snapshot is invalid.
1138    :raises NameTooLong: if the name of either snapshot is too long.
1139    '''
1140
1141    if origin is not None:
1142        c_origin = origin
1143    else:
1144        c_origin = _ffi.NULL
1145    if props is None:
1146        props = {}
1147    nvlist = nvlist_in(props)
1148    ret = _lib.lzc_receive_with_header(
1149        snapname, nvlist, c_origin, force, resumable, raw, fd, begin_record)
1150    errors.lzc_receive_translate_errors(
1151        ret, snapname, fd, force, raw, False, False, origin, None)
1152
1153
1154def receive_header(fd):
1155    '''
1156    Read the begin record of the ZFS backup stream from the given file
1157    descriptor.
1158
1159    This is a helper function for :func:`lzc_receive_with_header`.
1160
1161    :param int fd: the file descriptor from which to read the stream.
1162    :return: a tuple with two elements where the first one is a Python `dict`
1163        representing the fields of the begin record and the second one is an
1164        opaque object suitable for passing to :func:`lzc_receive_with_header`.
1165    :raises IOError: if an input / output error occurs while reading from the
1166        ``fd``.
1167
1168    At present the following fields can be of interest in the header:
1169
1170    drr_toname : bytes
1171        the name of the snapshot for which the stream has been created
1172    drr_toguid : integer
1173        the GUID of the snapshot for which the stream has been created
1174    drr_fromguid : integer
1175        the GUID of the starting snapshot in the case the stream is
1176        incremental, zero otherwise
1177    drr_flags : integer
1178        the flags describing the stream's properties
1179    drr_type : integer
1180        the type of the dataset for which the stream has been created
1181        (volume, filesystem)
1182    '''
1183    # read sizeof(dmu_replay_record_t) bytes directly into the memory backing
1184    # 'record'
1185    record = _ffi.new("dmu_replay_record_t *")
1186    _ffi.buffer(record)[:] = os.read(fd, _ffi.sizeof(record[0]))
1187    # get drr_begin member and its representation as a Python dict
1188    drr_begin = record.drr_u.drr_begin
1189    header = {}
1190    for field, descr in _ffi.typeof(drr_begin).fields:
1191        if descr.type.kind == 'primitive':
1192            header[field] = getattr(drr_begin, field)
1193        elif descr.type.kind == 'enum':
1194            header[field] = getattr(drr_begin, field)
1195        elif descr.type.kind == 'array' and descr.type.item.cname == 'char':
1196            header[field] = _ffi.string(getattr(drr_begin, field))
1197        else:
1198            raise TypeError(
1199                'Unexpected field type in drr_begin: ' + str(descr.type))
1200    return (header, record)
1201
1202
1203@_uncommitted()
1204def lzc_receive_one(
1205    snapname, fd, begin_record, force=False, resumable=False, raw=False,
1206    origin=None, props=None, cleanup_fd=-1, action_handle=0
1207):
1208    '''
1209    Like :func:`lzc_receive`, but allows the caller to pass all supported
1210    arguments and retrieve all values returned.  The only additional input
1211    parameter is 'cleanup_fd' which is used to set a cleanup-on-exit file
1212    descriptor.
1213
1214    :param bytes snapname: the name of the snapshot to create.
1215    :param int fd: the file descriptor from which to read the stream.
1216    :param begin_record: the stream's begin record.
1217    :type begin_record: ``cffi`` `CData` representing the dmu_replay_record_t
1218        structure.
1219    :param bool force: whether to roll back or destroy the target filesystem
1220        if that is required to receive the stream.
1221    :param bool resumable: whether this stream should be treated as resumable.
1222        If the receive fails due to premature stream termination, the
1223        intermediate state will be preserved on disk and may subsequently be
1224        resumed with :func:`lzc_send_resume`.
1225    :param bool raw: whether this is a "raw" stream.
1226    :param origin: the optional origin snapshot name if the stream is for a
1227        clone.
1228    :type origin: bytes or None
1229    :param props: the properties to set on the snapshot as *received*
1230        properties.
1231    :type props: dict of bytes : Any
1232    :param int cleanup_fd: file descriptor used to set a cleanup-on-exit file
1233        descriptor.
1234    :param int action_handle: variable used to pass the handle for guid/ds
1235        mapping: this should be set to zero on first call and will contain an
1236        updated handle on success, which should be passed in subsequent calls.
1237
1238    :return: a tuple with two elements where the first one is the number of
1239        bytes read from the file descriptor and the second one is the
1240        action_handle return value.
1241
1242    :raises IOError: if an input / output error occurs while reading from the
1243        ``fd``.
1244    :raises DatasetExists: if the snapshot named ``snapname`` already exists.
1245    :raises DatasetExists: if the stream is a full stream and the destination
1246        filesystem already exists.
1247    :raises DatasetExists: if ``force`` is `True` but the destination
1248        filesystem could not be rolled back to a matching snapshot because a
1249        newer snapshot exists and it is an origin of a cloned filesystem.
1250    :raises StreamMismatch: if an incremental stream is received and the latest
1251        snapshot of the destination filesystem does not match the source
1252        snapshot of the stream.
1253    :raises StreamMismatch: if a full stream is received and the destination
1254        filesystem already exists and it has at least one snapshot, and
1255        ``force`` is `False`.
1256    :raises StreamMismatch: if an incremental clone stream is received but the
1257        specified ``origin`` is not the actual received origin.
1258    :raises DestinationModified: if an incremental stream is received and the
1259        destination filesystem has been modified since the last snapshot and
1260        ``force`` is `False`.
1261    :raises DestinationModified: if a full stream is received and the
1262        destination filesystem already exists and it does not have any
1263        snapshots, and ``force`` is `False`.
1264    :raises DatasetNotFound: if the destination filesystem and its parent do
1265        not exist.
1266    :raises DatasetNotFound: if the ``origin`` is not `None` and does not
1267        exist.
1268    :raises DatasetBusy: if ``force`` is `True` but the destination filesystem
1269        could not be rolled back to a matching snapshot because a newer
1270        snapshot is held and could not be destroyed.
1271    :raises DatasetBusy: if another receive operation is being performed on the
1272        destination filesystem.
1273    :raises BadStream: if the stream is corrupt or it is not recognized or it
1274        is a compound stream or it is a clone stream, but ``origin`` is `None`.
1275    :raises BadStream: if a clone stream is received and the destination
1276        filesystem already exists.
1277    :raises StreamFeatureNotSupported: if the stream has a feature that is not
1278        supported on this side.
1279    :raises ReceivePropertyFailure: if one or more of the specified properties
1280        is invalid or has an invalid type or value.
1281    :raises NameInvalid: if the name of either snapshot is invalid.
1282    :raises NameTooLong: if the name of either snapshot is too long.
1283    '''
1284
1285    if origin is not None:
1286        c_origin = origin
1287    else:
1288        c_origin = _ffi.NULL
1289    if action_handle is not None:
1290        c_action_handle = _ffi.new("uint64_t *")
1291    else:
1292        c_action_handle = _ffi.NULL
1293    c_read_bytes = _ffi.new("uint64_t *")
1294    c_errflags = _ffi.new("uint64_t *")
1295    if props is None:
1296        props = {}
1297    nvlist = nvlist_in(props)
1298    properrs = {}
1299    with nvlist_out(properrs) as c_errors:
1300        ret = _lib.lzc_receive_one(
1301            snapname, nvlist, c_origin, force, resumable, raw, fd,
1302            begin_record, cleanup_fd, c_read_bytes, c_errflags,
1303            c_action_handle, c_errors)
1304    errors.lzc_receive_translate_errors(
1305        ret, snapname, fd, force, raw, False, False, origin, properrs)
1306    return (int(c_read_bytes[0]), action_handle)
1307
1308
1309@_uncommitted()
1310def lzc_receive_with_cmdprops(
1311    snapname, fd, begin_record, force=False, resumable=False, raw=False,
1312    origin=None, props=None, cmdprops=None, key=None, cleanup_fd=-1,
1313    action_handle=0
1314):
1315    '''
1316    Like :func:`lzc_receive_one`, but allows the caller to pass an additional
1317    'cmdprops' argument. The 'cmdprops' nvlist contains both override
1318    ('zfs receive -o') and exclude ('zfs receive -x') properties.
1319
1320    :param bytes snapname: the name of the snapshot to create.
1321    :param int fd: the file descriptor from which to read the stream.
1322    :param begin_record: the stream's begin record.
1323    :type begin_record: ``cffi`` `CData` representing the dmu_replay_record_t
1324        structure.
1325    :param bool force: whether to roll back or destroy the target filesystem
1326        if that is required to receive the stream.
1327    :param bool resumable: whether this stream should be treated as resumable.
1328        If the receive fails due to premature stream termination, the
1329        intermediate state will be preserved on disk and may subsequently be
1330        resumed with :func:`lzc_send_resume`.
1331    :param bool raw: whether this is a "raw" stream.
1332    :param origin: the optional origin snapshot name if the stream is for a
1333        clone.
1334    :type origin: bytes or None
1335    :param props: the properties to set on the snapshot as *received*
1336        properties.
1337    :type props: dict of bytes : Any
1338    :param cmdprops: the properties to set on the snapshot as local overrides
1339        to *received* properties. `bool` values are forcefully inherited while
1340        every other value is set locally as if the command "zfs set" was
1341        invoked immediately before the receive.
1342    :type cmdprops: dict of bytes : Any
1343    :param key: raw bytes representing user's wrapping key
1344    :type key: bytes
1345    :param int cleanup_fd: file descriptor used to set a cleanup-on-exit file
1346        descriptor.
1347    :param int action_handle: variable used to pass the handle for guid/ds
1348        mapping: this should be set to zero on first call and will contain an
1349        updated handle on success, it should be passed in subsequent calls.
1350
1351    :return: a tuple with two elements where the first one is the number of
1352        bytes read from the file descriptor and the second one is the
1353        action_handle return value.
1354
1355    :raises IOError: if an input / output error occurs while reading from the
1356        ``fd``.
1357    :raises DatasetExists: if the snapshot named ``snapname`` already exists.
1358    :raises DatasetExists: if the stream is a full stream and the destination
1359        filesystem already exists.
1360    :raises DatasetExists: if ``force`` is `True` but the destination
1361        filesystem could not be rolled back to a matching snapshot because a
1362        newer snapshot exists and it is an origin of a cloned filesystem.
1363    :raises StreamMismatch: if an incremental stream is received and the latest
1364        snapshot of the destination filesystem does not match the source
1365        snapshot of the stream.
1366    :raises StreamMismatch: if a full stream is received and the destination
1367        filesystem already exists and it has at least one snapshot, and
1368        ``force`` is `False`.
1369    :raises StreamMismatch: if an incremental clone stream is received but the
1370        specified ``origin`` is not the actual received origin.
1371    :raises DestinationModified: if an incremental stream is received and the
1372        destination filesystem has been modified since the last snapshot and
1373        ``force`` is `False`.
1374    :raises DestinationModified: if a full stream is received and the
1375        destination filesystem already exists and it does not have any
1376        snapshots, and ``force`` is `False`.
1377    :raises DatasetNotFound: if the destination filesystem and its parent do
1378        not exist.
1379    :raises DatasetNotFound: if the ``origin`` is not `None` and does not
1380        exist.
1381    :raises DatasetBusy: if ``force`` is `True` but the destination filesystem
1382        could not be rolled back to a matching snapshot because a newer
1383        snapshot is held and could not be destroyed.
1384    :raises DatasetBusy: if another receive operation is being performed on the
1385        destination filesystem.
1386    :raises BadStream: if the stream is corrupt or it is not recognized or it
1387        is a compound stream or it is a clone stream, but ``origin`` is `None`.
1388    :raises BadStream: if a clone stream is received and the destination
1389        filesystem already exists.
1390    :raises StreamFeatureNotSupported: if the stream has a feature that is not
1391        supported on this side.
1392    :raises ReceivePropertyFailure: if one or more of the specified properties
1393        is invalid or has an invalid type or value.
1394    :raises NameInvalid: if the name of either snapshot is invalid.
1395    :raises NameTooLong: if the name of either snapshot is too long.
1396    '''
1397
1398    if origin is not None:
1399        c_origin = origin
1400    else:
1401        c_origin = _ffi.NULL
1402    if action_handle is not None:
1403        c_action_handle = _ffi.new("uint64_t *")
1404    else:
1405        c_action_handle = _ffi.NULL
1406    c_read_bytes = _ffi.new("uint64_t *")
1407    c_errflags = _ffi.new("uint64_t *")
1408    if props is None:
1409        props = {}
1410    if cmdprops is None:
1411        cmdprops = {}
1412    if key is None:
1413        key = b""
1414    else:
1415        key = bytes(key)
1416
1417    nvlist = nvlist_in(props)
1418    cmdnvlist = nvlist_in(cmdprops)
1419    properrs = {}
1420    with nvlist_out(properrs) as c_errors:
1421        ret = _lib.lzc_receive_with_cmdprops(
1422            snapname, nvlist, cmdnvlist, key, len(key), c_origin,
1423            force, resumable, raw, fd, begin_record, cleanup_fd, c_read_bytes,
1424            c_errflags, c_action_handle, c_errors)
1425    errors.lzc_receive_translate_errors(
1426        ret, snapname, fd, force, raw, False, False, origin, properrs)
1427    return (int(c_read_bytes[0]), action_handle)
1428
1429
1430@_uncommitted()
1431def lzc_receive_with_heal(
1432    snapname, fd, begin_record, force=False, corrective=True, resumable=False,
1433    raw=False, origin=None, props=None, cmdprops=None, key=None, cleanup_fd=-1,
1434    action_handle=0
1435):
1436    '''
1437    Like :func:`lzc_receive_cmdprops`, but allows the caller to pass an
1438    additional 'corrective' argument. The 'corrective' boolean set to true
1439    indicates that a corruption healing receive should be performed.
1440
1441    :param bytes snapname: the name of the snapshot to create.
1442    :param int fd: the file descriptor from which to read the stream.
1443    :param begin_record: the stream's begin record.
1444    :type begin_record: ``cffi`` `CData` representing the dmu_replay_record_t
1445        structure.
1446    :param bool force: whether to roll back or destroy the target filesystem
1447        if that is required to receive the stream.
1448    :param bool corrective: whether this stream should be used to heal data.
1449    :param bool resumable: whether this stream should be treated as resumable.
1450        If the receive fails due to premature stream termination, the
1451        intermediate state will be preserved on disk and may subsequently be
1452        resumed with :func:`lzc_send_resume`.
1453    :param bool raw: whether this is a "raw" stream.
1454    :param origin: the optional origin snapshot name if the stream is for a
1455        clone.
1456    :type origin: bytes or None
1457    :param props: the properties to set on the snapshot as *received*
1458        properties.
1459    :type props: dict of bytes : Any
1460    :param cmdprops: the properties to set on the snapshot as local overrides
1461        to *received* properties. `bool` values are forcefully inherited while
1462        every other value is set locally as if the command "zfs set" was
1463        invoked immediately before the receive.
1464    :type cmdprops: dict of bytes : Any
1465    :param key: raw bytes representing user's wrapping key
1466    :type key: bytes
1467    :param int cleanup_fd: file descriptor used to set a cleanup-on-exit file
1468        descriptor.
1469    :param int action_handle: variable used to pass the handle for guid/ds
1470        mapping: this should be set to zero on first call and will contain an
1471        updated handle on success, it should be passed in subsequent calls.
1472
1473    :return: a tuple with two elements where the first one is the number of
1474        bytes read from the file descriptor and the second one is the
1475        action_handle return value.
1476
1477    :raises IOError: if an input / output error occurs while reading from the
1478        ``fd``.
1479    :raises DatasetExists: if the snapshot named ``snapname`` already exists.
1480    :raises DatasetExists: if the stream is a full stream and the destination
1481        filesystem already exists.
1482    :raises DatasetExists: if ``force`` is `True` but the destination
1483        filesystem could not be rolled back to a matching snapshot because a
1484        newer snapshot exists and it is an origin of a cloned filesystem.
1485    :raises StreamMismatch: if an incremental stream is received and the latest
1486        snapshot of the destination filesystem does not match the source
1487        snapshot of the stream.
1488    :raises StreamMismatch: if a full stream is received and the destination
1489        filesystem already exists and it has at least one snapshot, and
1490        ``force`` is `False`.
1491    :raises StreamMismatch: if an incremental clone stream is received but the
1492        specified ``origin`` is not the actual received origin.
1493    :raises DestinationModified: if an incremental stream is received and the
1494        destination filesystem has been modified since the last snapshot and
1495        ``force`` is `False`.
1496    :raises DestinationModified: if a full stream is received and the
1497        destination filesystem already exists and it does not have any
1498        snapshots, and ``force`` is `False`.
1499    :raises DatasetNotFound: if the destination filesystem and its parent do
1500        not exist.
1501    :raises DatasetNotFound: if the ``origin`` is not `None` and does not
1502        exist.
1503    :raises DatasetBusy: if ``force`` is `True` but the destination filesystem
1504        could not be rolled back to a matching snapshot because a newer
1505        snapshot is held and could not be destroyed.
1506    :raises DatasetBusy: if another receive operation is being performed on the
1507        destination filesystem.
1508    :raises EncryptionKeyNotLoaded: if corrective is set to true indicates the
1509            key must be loaded to do a non-raw corrective recv on an encrypted
1510            dataset.
1511    :raises BadStream: if corrective is set to true indicates that
1512        corrective recv was not able to reconstruct a corrupted block.
1513    :raises BadStream: if the stream is corrupt or it is not recognized or it
1514        is a compound stream or it is a clone stream, but ``origin`` is `None`.
1515    :raises BadStream: if a clone stream is received and the destination
1516        filesystem already exists.
1517    :raises StreamFeatureNotSupported: if corrective is set to true indicates
1518        stream is not compatible with the data in the pool.
1519    :raises StreamFeatureNotSupported: if the stream has a feature that is not
1520        supported on this side.
1521    :raises ReceivePropertyFailure: if one or more of the specified properties
1522        is invalid or has an invalid type or value.
1523    :raises NameInvalid: if the name of either snapshot is invalid.
1524    :raises NameTooLong: if the name of either snapshot is too long.
1525    '''
1526
1527    if origin is not None:
1528        c_origin = origin
1529    else:
1530        c_origin = _ffi.NULL
1531    if action_handle is not None:
1532        c_action_handle = _ffi.new("uint64_t *")
1533    else:
1534        c_action_handle = _ffi.NULL
1535    c_read_bytes = _ffi.new("uint64_t *")
1536    c_errflags = _ffi.new("uint64_t *")
1537    if props is None:
1538        props = {}
1539    if cmdprops is None:
1540        cmdprops = {}
1541    if key is None:
1542        key = b""
1543    else:
1544        key = bytes(key)
1545
1546    nvlist = nvlist_in(props)
1547    cmdnvlist = nvlist_in(cmdprops)
1548    properrs = {}
1549    with nvlist_out(properrs) as c_errors:
1550        ret = _lib.lzc_receive_with_heal(
1551            snapname, nvlist, cmdnvlist, key, len(key), c_origin,
1552            force, corrective, resumable, raw, fd, begin_record, cleanup_fd,
1553            c_read_bytes, c_errflags, c_action_handle, c_errors)
1554    errors.lzc_receive_translate_errors(
1555        ret, snapname, fd, force, raw, False, False, origin, properrs)
1556    return (int(c_read_bytes[0]), action_handle)
1557
1558
1559@_uncommitted()
1560def lzc_reopen(poolname, restart=True):
1561    '''
1562    Reopen a pool
1563
1564    :param bytes poolname: the name of the pool.
1565    :param bool restart: whether to restart an in-progress scrub operation.
1566
1567    :raises PoolNotFound: if the pool does not exist.
1568    '''
1569    ret = _lib.lzc_reopen(poolname, restart)
1570    errors.lzc_reopen_translate_error(ret, poolname)
1571
1572
1573def lzc_send_resume(
1574    snapname, fromsnap, fd, flags=None, resumeobj=0, resumeoff=0
1575):
1576    '''
1577    Resume a previously interrupted send operation generating a zfs send stream
1578    for the specified snapshot and writing it to the specified file descriptor.
1579
1580    :param bytes snapname: the name of the snapshot to send.
1581    :param fromsnap: if not None the name of the starting snapshot
1582        for the incremental stream.
1583    :type fromsnap: bytes or None
1584    :param int fd: the file descriptor to write the send stream to.
1585    :param flags: the flags that control what enhanced features can be used in
1586        the stream.
1587    :type flags: list of bytes
1588    :param int resumeobj: the object number where this send stream should
1589        resume from.
1590    :param int resumeoff: the offset where this send stream should resume from.
1591
1592    :raises SnapshotNotFound: if either the starting snapshot is not `None` and
1593        does not exist, or if the ending snapshot does not exist.
1594    :raises NameInvalid: if the name of either snapshot is invalid.
1595    :raises NameTooLong: if the name of either snapshot is too long.
1596    :raises SnapshotMismatch: if ``fromsnap`` is not an ancestor snapshot of
1597        ``snapname``.
1598    :raises PoolsDiffer: if the snapshots belong to different pools.
1599    :raises IOError: if an input / output error occurs while writing to ``fd``.
1600    :raises UnknownStreamFeature: if the ``flags`` contain an unknown flag
1601        name.
1602
1603    .. note::
1604        See :func:`lzc_send` for more information.
1605    '''
1606    if fromsnap is not None:
1607        c_fromsnap = fromsnap
1608    else:
1609        c_fromsnap = _ffi.NULL
1610    c_flags = 0
1611    if flags is None:
1612        flags = []
1613    for flag in flags:
1614        c_flag = {
1615            'embedded_data': _lib.LZC_SEND_FLAG_EMBED_DATA,
1616            'large_blocks': _lib.LZC_SEND_FLAG_LARGE_BLOCK,
1617            'compress': _lib.LZC_SEND_FLAG_COMPRESS,
1618            'raw': _lib.LZC_SEND_FLAG_RAW,
1619        }.get(flag)
1620        if c_flag is None:
1621            raise exceptions.UnknownStreamFeature(flag)
1622        c_flags |= c_flag
1623
1624    ret = _lib.lzc_send_resume(
1625        snapname, c_fromsnap, fd, c_flags, uint64_t(resumeobj),
1626        uint64_t(resumeoff))
1627    errors.lzc_send_translate_error(ret, snapname, fromsnap, fd, flags)
1628
1629
1630@_uncommitted()
1631def lzc_sync(poolname, force=False):
1632    '''
1633    Forces all in-core dirty data to be written to the primary pool storage
1634    and not the ZIL.
1635
1636    :param bytes poolname: the name of the pool.
1637    :param bool force: whether to force uberblock update even if there is no
1638        dirty data.
1639
1640    :raises PoolNotFound: if the pool does not exist.
1641
1642    .. note::
1643        This method signature is different from its C libzfs_core counterpart:
1644        `innvl` has been replaced by the `force` boolean and `outnvl` has been
1645        conveniently removed since it's not used.
1646    '''
1647    innvl = nvlist_in({b"force": force})
1648    with nvlist_out({}) as outnvl:
1649        ret = _lib.lzc_sync(poolname, innvl, outnvl)
1650    errors.lzc_sync_translate_error(ret, poolname)
1651
1652
1653def is_supported(func):
1654    '''
1655    Check whether C *libzfs_core* provides implementation required
1656    for the given Python wrapper.
1657
1658    If `is_supported` returns ``False`` for the function, then
1659    calling the function would result in :exc:`NotImplementedError`.
1660
1661    :param function func: the function to check.
1662    :return bool: whether the function can be used.
1663    '''
1664    fname = func.__name__
1665    if fname not in globals():
1666        raise ValueError(fname + ' is not from libzfs_core')
1667    if not callable(func):
1668        raise ValueError(fname + ' is not a function')
1669    if not fname.startswith("lzc_"):
1670        raise ValueError(fname + ' is not a libzfs_core API function')
1671    check_func = getattr(func, "_check_func", None)
1672    if check_func is not None:
1673        return is_supported(check_func)
1674    return getattr(_lib, fname, None) is not None
1675
1676
1677@_uncommitted()
1678def lzc_promote(name):
1679    '''
1680    Promotes the ZFS dataset.
1681
1682    :param bytes name: the name of the dataset to promote.
1683    :raises NameInvalid: if the dataset name is invalid.
1684    :raises NameTooLong: if the dataset name is too long.
1685    :raises NameTooLong: if the dataset's origin has a snapshot that, if
1686        transferred to the dataset, would get a too long name.
1687    :raises NotClone: if the dataset is not a clone.
1688    :raises FilesystemNotFound: if the dataset does not exist.
1689    :raises SnapshotExists: if the dataset already has a snapshot with the same
1690        name as one of the origin's snapshots.
1691    '''
1692    ret = _lib.lzc_promote(name, _ffi.NULL, _ffi.NULL)
1693    errors.lzc_promote_translate_error(ret, name)
1694
1695
1696@_uncommitted()
1697def lzc_pool_checkpoint(name):
1698    '''
1699    Creates a checkpoint for the specified pool.
1700
1701    :param bytes name: the name of the pool to create a checkpoint for.
1702    :raises CheckpointExists: if the pool already has a checkpoint.
1703    :raises CheckpointDiscarding: if ZFS is in the middle of discarding a
1704        checkpoint for this pool.
1705    :raises DeviceRemovalRunning: if a vdev is currently being removed.
1706    :raises DeviceTooBig: if one or more top-level vdevs exceed the maximum
1707        vdev size.
1708    '''
1709    ret = _lib.lzc_pool_checkpoint(name)
1710    errors.lzc_pool_checkpoint_translate_error(ret, name)
1711
1712
1713@_uncommitted()
1714def lzc_pool_checkpoint_discard(name):
1715    '''
1716    Discard the checkpoint from the specified pool.
1717
1718    :param bytes name: the name of the pool to discard the checkpoint from.
1719    :raises CheckpointNotFound: if pool does not have a checkpoint.
1720    :raises CheckpointDiscarding: if ZFS is in the middle of discarding a
1721        checkpoint for this pool.
1722    '''
1723    ret = _lib.lzc_pool_checkpoint_discard(name)
1724    errors.lzc_pool_checkpoint_discard_translate_error(ret, name)
1725
1726
1727def lzc_rename(source, target):
1728    '''
1729    Rename the ZFS dataset.
1730
1731    :param source name: the current name of the dataset to rename.
1732    :param target name: the new name of the dataset.
1733    :raises NameInvalid: if either the source or target name is invalid.
1734    :raises NameTooLong: if either the source or target name is too long.
1735    :raises NameTooLong: if a snapshot of the source would get a too long name
1736        after renaming.
1737    :raises FilesystemNotFound: if the source does not exist.
1738    :raises FilesystemNotFound: if the target's parent does not exist.
1739    :raises FilesystemExists: if the target already exists.
1740    :raises PoolsDiffer: if the source and target belong to different pools.
1741    :raises WrongParent: if the "new" parent dataset is not a filesystem
1742        (e.g. ZVOL)
1743    '''
1744    ret = _lib.lzc_rename(source, target)
1745    errors.lzc_rename_translate_error(ret, source, target)
1746
1747
1748def lzc_destroy(name):
1749    '''
1750    Destroy the ZFS dataset.
1751
1752    :param bytes name: the name of the dataset to destroy.
1753    :raises NameInvalid: if the dataset name is invalid.
1754    :raises NameTooLong: if the dataset name is too long.
1755    :raises FilesystemNotFound: if the dataset does not exist.
1756    '''
1757    ret = _lib.lzc_destroy(name)
1758    errors.lzc_destroy_translate_error(ret, name)
1759
1760
1761@_uncommitted()
1762def lzc_inherit(name, prop):
1763    '''
1764    Inherit properties from a parent dataset of the given ZFS dataset.
1765
1766    :param bytes name: the name of the dataset.
1767    :param bytes prop: the name of the property to inherit.
1768    :raises NameInvalid: if the dataset name is invalid.
1769    :raises NameTooLong: if the dataset name is too long.
1770    :raises DatasetNotFound: if the dataset does not exist.
1771    :raises PropertyInvalid: if one or more of the specified properties is
1772        invalid or has an invalid type or value.
1773
1774    Inheriting a property actually resets it to its default value
1775    or removes it if it's a user property, so that the property could be
1776    inherited if it's inheritable.  If the property is not inheritable
1777    then it would just have its default value.
1778
1779    This function can be used on snapshots to inherit user defined properties.
1780    '''
1781    ret = _lib.lzc_inherit(name, prop, _ffi.NULL)
1782    errors.lzc_inherit_prop_translate_error(ret, name, prop)
1783
1784
1785# As the extended API is not committed yet, the names of the new interfaces
1786# are not settled down yet.
1787# lzc_inherit_prop makes it clearer what is to be inherited.
1788lzc_inherit_prop = lzc_inherit
1789
1790
1791@_uncommitted()
1792def lzc_set_props(name, prop, val):
1793    '''
1794    Set properties of the ZFS dataset.
1795
1796    :param bytes name: the name of the dataset.
1797    :param bytes prop: the name of the property.
1798    :param Any val: the value of the property.
1799    :raises NameInvalid: if the dataset name is invalid.
1800    :raises NameTooLong: if the dataset name is too long.
1801    :raises DatasetNotFound: if the dataset does not exist.
1802    :raises NoSpace: if the property controls a quota and the values is too
1803        small for that quota.
1804    :raises PropertyInvalid: if one or more of the specified properties is
1805        invalid or has an invalid type or value.
1806
1807    This function can be used on snapshots to set user defined properties.
1808
1809    .. note::
1810        An attempt to set a readonly / statistic property is ignored
1811        without reporting any error.
1812    '''
1813    props = {prop: val}
1814    props_nv = nvlist_in(props)
1815    ret = _lib.lzc_set_props(name, props_nv, _ffi.NULL, _ffi.NULL)
1816    errors.lzc_set_prop_translate_error(ret, name, prop, val)
1817
1818
1819# As the extended API is not committed yet, the names of the new interfaces
1820# are not settled down yet.
1821# It's not clear if atomically setting multiple properties is an achievable
1822# goal and an interface acting on multiple entities must do so atomically
1823# by convention.
1824# Being able to set a single property at a time is sufficient for ClusterHQ.
1825lzc_set_prop = lzc_set_props
1826
1827
1828@_uncommitted()
1829def lzc_list(name, options):
1830    '''
1831    List subordinate elements of the given dataset.
1832
1833    This function can be used to list child datasets and snapshots of the given
1834    dataset.  The listed elements can be filtered by their type and by their
1835    depth relative to the starting dataset.
1836
1837    :param bytes name: the name of the dataset to be listed, could be a
1838        snapshot or a dataset.
1839    :param options: a `dict` of the options that control the listing behavior.
1840    :type options: dict of bytes:Any
1841    :return: a pair of file descriptors the first of which can be used to read
1842        the listing.
1843    :rtype: tuple of (int, int)
1844    :raises DatasetNotFound: if the dataset does not exist.
1845
1846    Two options are currently available:
1847
1848    recurse : integer or None
1849        specifies depth of the recursive listing. If ``None`` the depth is not
1850        limited.
1851        Absence of this option means that only the given dataset is listed.
1852
1853    type : dict of bytes:None
1854        specifies dataset types to include into the listing.
1855        Currently allowed keys are "filesystem", "volume", "snapshot".
1856        Absence of this option implies all types.
1857
1858    The first of the returned file descriptors can be used to
1859    read the listing in a binary encoded format.  The data is
1860    a series of variable sized records each starting with a fixed
1861    size header, the header is followed by a serialized ``nvlist``.
1862    Each record describes a single element and contains the element's
1863    name as well as its properties.
1864    The file descriptor must be closed after reading from it.
1865
1866    The second file descriptor represents a pipe end to which the
1867    kernel driver is writing information.  It should not be closed
1868    until all interesting information has been read and it must
1869    be explicitly closed afterwards.
1870    '''
1871    (rfd, wfd) = os.pipe()
1872    fcntl.fcntl(rfd, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
1873    fcntl.fcntl(wfd, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
1874    options = options.copy()
1875    options['fd'] = int32_t(wfd)
1876    opts_nv = nvlist_in(options)
1877    ret = _lib.lzc_list(name, opts_nv)
1878    if ret == errno.ESRCH:
1879        return (None, None)
1880    errors.lzc_list_translate_error(ret, name, options)
1881    return (rfd, wfd)
1882
1883
1884# Description of the binary format used to pass data from the kernel.
1885_PIPE_RECORD_FORMAT = 'IBBBB'
1886_PIPE_RECORD_SIZE = struct.calcsize(_PIPE_RECORD_FORMAT)
1887
1888
1889def _list(name, recurse=None, types=None):
1890    '''
1891    A wrapper for :func:`lzc_list` that hides details of working
1892    with the file descriptors and provides data in an easy to
1893    consume format.
1894
1895    :param bytes name: the name of the dataset to be listed, could be a
1896        snapshot, a volume or a filesystem.
1897    :param recurse: specifies depth of the recursive listing. If ``None`` the
1898        depth is not limited.
1899    :param types: specifies dataset types to include into the listing.
1900        Currently allowed keys are "filesystem", "volume", "snapshot". ``None``
1901        is equivalent to specifying the type of the dataset named by `name`.
1902    :type types: list of bytes or None
1903    :type recurse: integer or None
1904    :return: a list of dictionaries each describing a single listed element.
1905    :rtype: list of dict
1906    '''
1907    options = {}
1908
1909    # Convert types to a dict suitable for mapping to an nvlist.
1910    if types is not None:
1911        types = {x: None for x in types}
1912        options['type'] = types
1913    if recurse is None or recurse > 0:
1914        options['recurse'] = recurse
1915
1916    # Note that other_fd is used by the kernel side to write
1917    # the data, so we have to keep that descriptor open until
1918    # we are done.
1919    # Also, we have to explicitly close the descriptor as the
1920    # kernel doesn't do that.
1921    (fd, other_fd) = lzc_list(name, options)
1922    if fd is None:
1923        return
1924
1925    try:
1926        while True:
1927            record_bytes = os.read(fd, _PIPE_RECORD_SIZE)
1928            if not record_bytes:
1929                break
1930            (size, _, err, _, _) = struct.unpack(
1931                _PIPE_RECORD_FORMAT, record_bytes)
1932            if err == errno.ESRCH:
1933                break
1934            errors.lzc_list_translate_error(err, name, options)
1935            if size == 0:
1936                break
1937            data_bytes = os.read(fd, size)
1938            result = {}
1939            with nvlist_out(result) as nvp:
1940                ret = _lib.nvlist_unpack(data_bytes, size, nvp, 0)
1941            if ret != 0:
1942                raise exceptions.ZFSGenericError(
1943                    ret, None, "Failed to unpack list data")
1944            yield result
1945    finally:
1946        os.close(other_fd)
1947        os.close(fd)
1948
1949
1950@_uncommitted(lzc_list)
1951def lzc_get_props(name):
1952    '''
1953    Get properties of the ZFS dataset.
1954
1955    :param bytes name: the name of the dataset.
1956    :raises DatasetNotFound: if the dataset does not exist.
1957    :raises NameInvalid: if the dataset name is invalid.
1958    :raises NameTooLong: if the dataset name is too long.
1959    :return: a dictionary mapping the property names to their values.
1960    :rtype: dict of bytes:Any
1961
1962    .. note::
1963        The value of ``clones`` property is a `list` of clone names as byte
1964        strings.
1965
1966    .. warning::
1967        The returned dictionary does not contain entries for properties
1968        with default values.  One exception is the ``mountpoint`` property
1969        for which the default value is derived from the dataset name.
1970    '''
1971    result = next(_list(name, recurse=0))
1972    is_snapshot = result['dmu_objset_stats']['dds_is_snapshot']
1973    result = result['properties']
1974    # In most cases the source of the property is uninteresting and the
1975    # value alone is sufficient.  One exception is the 'mountpoint'
1976    # property the final value of which is not the same as the inherited
1977    # value.
1978    mountpoint = result.get('mountpoint')
1979    if mountpoint is not None:
1980        mountpoint_src = mountpoint['source']
1981        mountpoint_val = mountpoint['value']
1982        # 'source' is the name of the dataset that has 'mountpoint' set
1983        # to a non-default value and from which the current dataset inherits
1984        # the property.  'source' can be the current dataset if its
1985        # 'mountpoint' is explicitly set.
1986        # 'source' can also be a special value like '$recvd', that case
1987        # is equivalent to the property being set on the current dataset.
1988        # Note that a normal mountpoint value should start with '/'
1989        # unlike the special values "none" and "legacy".
1990        if (mountpoint_val.startswith('/') and
1991                not mountpoint_src.startswith('$')):
1992            mountpoint_val = mountpoint_val + name[len(mountpoint_src):]
1993    elif not is_snapshot:
1994        mountpoint_val = '/' + name
1995    else:
1996        mountpoint_val = None
1997    result = {k: result[k]['value'] for k in result}
1998    if 'clones' in result:
1999        result['clones'] = list(result['clones'].keys())
2000    if mountpoint_val is not None:
2001        result['mountpoint'] = mountpoint_val
2002    return result
2003
2004
2005@_uncommitted(lzc_list)
2006def lzc_list_children(name):
2007    '''
2008    List the children of the ZFS dataset.
2009
2010    :param bytes name: the name of the dataset.
2011    :return: an iterator that produces the names of the children.
2012    :raises NameInvalid: if the dataset name is invalid.
2013    :raises NameTooLong: if the dataset name is too long.
2014    :raises DatasetNotFound: if the dataset does not exist.
2015
2016    .. warning::
2017        If the dataset does not exist, then the returned iterator would produce
2018        no results and no error is reported.
2019        That case is indistinguishable from the dataset having no children.
2020
2021        An attempt to list children of a snapshot is silently ignored as well.
2022    '''
2023    children = []
2024    for entry in _list(name, recurse=1, types=['filesystem', 'volume']):
2025        child = entry['name']
2026        if child != name:
2027            children.append(child)
2028
2029    return iter(children)
2030
2031
2032@_uncommitted(lzc_list)
2033def lzc_list_snaps(name):
2034    '''
2035    List the snapshots of the ZFS dataset.
2036
2037    :param bytes name: the name of the dataset.
2038    :return: an iterator that produces the names of the snapshots.
2039    :raises NameInvalid: if the dataset name is invalid.
2040    :raises NameTooLong: if the dataset name is too long.
2041    :raises DatasetNotFound: if the dataset does not exist.
2042
2043    .. warning::
2044        If the dataset does not exist, then the returned iterator would produce
2045        no results and no error is reported.
2046        That case is indistinguishable from the dataset having no snapshots.
2047
2048        An attempt to list snapshots of a snapshot is silently ignored as well.
2049    '''
2050    snaps = []
2051    for entry in _list(name, recurse=1, types=['snapshot']):
2052        snap = entry['name']
2053        if snap != name:
2054            snaps.append(snap)
2055
2056    return iter(snaps)
2057
2058
2059# TODO: a better way to init and uninit the library
2060def _initialize():
2061    class LazyInit(object):
2062
2063        def __init__(self, lib):
2064            self._lib = lib
2065            self._inited = False
2066            self._lock = threading.Lock()
2067
2068        def __getattr__(self, name):
2069            if not self._inited:
2070                with self._lock:
2071                    if not self._inited:
2072                        ret = self._lib.libzfs_core_init()
2073                        if ret != 0:
2074                            raise exceptions.ZFSInitializationFailed(ret)
2075                        self._inited = True
2076            return getattr(self._lib, name)
2077
2078    return LazyInit(libzfs_core.lib)
2079
2080
2081_ffi = libzfs_core.ffi
2082_lib = _initialize()
2083
2084
2085# vim: softtabstop=4 tabstop=4 expandtab shiftwidth=4
2086