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