zfs_core/
lib.rs

1#![warn(missing_debug_implementations, rust_2018_idioms)]
2
3use cstr_argument::CStrArgument;
4use foreign_types::ForeignType;
5use nvpair::{NvList, NvListIter, NvListRef};
6use snafu::Snafu;
7use std::convert::TryInto;
8use std::marker::PhantomData;
9use std::os::unix::io::RawFd;
10use std::{ffi, fmt, io, ptr};
11use zfs_core_sys as sys;
12
13// TODO: consider splitting this into specific error kinds per operation
14#[derive(Debug, Snafu)]
15pub enum Error {
16    #[snafu(display("libzfs_core call failed with {}", source))]
17    Io { source: io::Error },
18    #[snafu(display("libzfs_core call failed for these entries {}", source))]
19    List { source: ErrorList },
20}
21
22//pub type Result<T, E = Error> = std::result::Result<T, E>;
23
24/// A handle to work with Zfs pools, datasets, etc
25// Note: the Drop for this makes clone-by-copy unsafe. Could clone by just calling new().
26//
27// Internally, libzfs_core maintains a refcount for the `libzfs_core_init()` and
28// `libzfs_core_fini()` calls, so we need the init to match fini. Alternatively, we could use a
29// single init and never fini.
30pub struct Zfs {
31    i: PhantomData<()>,
32}
33
34impl fmt::Debug for Zfs {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        f.debug_struct("Zfs").finish()
37    }
38}
39
40#[derive(Debug)]
41pub enum DataSetType {
42    Zfs,
43    Zvol,
44}
45
46impl DataSetType {
47    fn as_raw(&self) -> ::std::os::raw::c_uint {
48        match self {
49            DataSetType::Zfs => sys::lzc_dataset_type::LZC_DATSET_TYPE_ZFS,
50            DataSetType::Zvol => sys::lzc_dataset_type::LZC_DATSET_TYPE_ZVOL,
51        }
52    }
53}
54
55/// Generic list of errors return from various `lzc_*` calls.
56///
57/// The first item (the `name`) is the thing we were operating on (creating, destroying, etc) that cause the error,
58/// and the second (the `error`) is the translated error code
59///
60/// Note that there is a special `name` "N_MORE_ERRORS" which is a count of errors not listed
61#[derive(Debug)]
62pub struct ErrorList {
63    nv: NvList,
64}
65
66impl ErrorList {
67    pub fn iter(&self) -> ErrorListIter<'_> {
68        self.into_iter()
69    }
70}
71
72impl std::error::Error for ErrorList {}
73
74impl From<NvList> for ErrorList {
75    fn from(nv: NvList) -> Self {
76        // TODO: consider examining shape of the error list here
77        Self { nv }
78    }
79}
80
81impl AsRef<NvList> for ErrorList {
82    fn as_ref(&self) -> &NvList {
83        &self.nv
84    }
85}
86
87impl AsMut<NvList> for ErrorList {
88    fn as_mut(&mut self) -> &mut NvList {
89        &mut self.nv
90    }
91}
92
93impl<'a> IntoIterator for &'a ErrorList {
94    type Item = (&'a ffi::CStr, io::Error);
95    type IntoIter = ErrorListIter<'a>;
96
97    fn into_iter(self) -> Self::IntoIter {
98        ErrorListIter {
99            nvi: self.nv.iter(),
100        }
101    }
102}
103
104#[derive(Debug, Clone)]
105pub struct ErrorListIter<'a> {
106    nvi: NvListIter<'a>,
107}
108
109impl<'a> fmt::Display for ErrorListIter<'a> {
110    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
111        fmt.debug_list().entries(self.clone()).finish()
112    }
113}
114
115impl fmt::Display for ErrorList {
116    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
117        fmt::Debug::fmt(&self.into_iter(), fmt)
118    }
119}
120
121impl<'a> Iterator for ErrorListIter<'a> {
122    type Item = (&'a ffi::CStr, io::Error);
123
124    fn next(&mut self) -> Option<Self::Item> {
125        match self.nvi.next() {
126            Some(np) => {
127                let name = np.name();
128                let data = np.data();
129
130                match data {
131                    nvpair::NvData::Int32(v) => Some((name, io::Error::from_raw_os_error(v))),
132                    _ => {
133                        // TODO: consider validating early. alternately: consider emitting
134                        // something reasonable here. we're already an error path, so being 100%
135                        // precise is probably not required.
136                        panic!("unhandled error type for name {:?}: {:?}", name, data);
137                    }
138                }
139            }
140            None => None,
141        }
142    }
143}
144
145impl Zfs {
146    /// Create a handle to the Zfs subsystem
147    #[doc(alias = "libzfs_core_init")]
148    pub fn new() -> io::Result<Self> {
149        let v = unsafe { sys::libzfs_core_init() };
150
151        if v != 0 {
152            Err(io::Error::from_raw_os_error(v))
153        } else {
154            Ok(Self { i: PhantomData })
155        }
156    }
157
158    /// Create a new dataset of the given type with `props` set as properties
159    ///
160    /// Corresponds to `lzc_create()`
161    #[doc(alias = "lzc_create")]
162    pub fn create<S: CStrArgument>(
163        &self,
164        name: S,
165        dataset_type: DataSetType,
166        props: &NvList,
167    ) -> io::Result<()> {
168        let name = name.into_cstr();
169        let v = unsafe {
170            sys::lzc_create(
171                name.as_ref().as_ptr(),
172                dataset_type.as_raw(),
173                props.as_ptr() as *mut _,
174                ptr::null_mut(),
175                0,
176            )
177        };
178
179        if v != 0 {
180            Err(io::Error::from_raw_os_error(v))
181        } else {
182            Ok(())
183        }
184    }
185
186    /// Corresponds to `lzc_clone()`
187    #[doc(alias = "lzc_clone")]
188    pub fn clone_dataset<S: CStrArgument, S2: CStrArgument>(
189        &self,
190        name: S,
191        origin: S2,
192        props: &mut NvListRef,
193    ) -> io::Result<()> {
194        let name = name.into_cstr();
195        let origin = origin.into_cstr();
196        let v = unsafe {
197            sys::lzc_clone(
198                name.as_ref().as_ptr(),
199                origin.as_ref().as_ptr(),
200                props.as_mut_ptr(),
201            )
202        };
203        if v != 0 {
204            Err(io::Error::from_raw_os_error(v))
205        } else {
206            Ok(())
207        }
208    }
209
210    // TODO: avoid using an out-param for `snap_name_buf`
211    // `snap_name_buf` is filled by a cstring
212    /// Corresponds to `lzc_promote()`
213    #[doc(alias = "lzc_promote")]
214    pub fn promote<S: CStrArgument>(&self, fsname: S, snap_name_buf: &mut [u8]) -> io::Result<()> {
215        let fsname = fsname.into_cstr();
216        let v = unsafe {
217            sys::lzc_promote(
218                fsname.as_ref().as_ptr(),
219                snap_name_buf.as_mut_ptr() as *mut _,
220                snap_name_buf.len().try_into().unwrap(),
221            )
222        };
223        if v != 0 {
224            Err(io::Error::from_raw_os_error(v))
225        } else {
226            Ok(())
227        }
228    }
229
230    /// Corresponds to `lzc_rename()`
231    #[doc(alias = "lzc_rename")]
232    pub fn rename<S: CStrArgument, T: CStrArgument>(&self, source: S, target: T) -> io::Result<()> {
233        let source = source.into_cstr();
234        let target = target.into_cstr();
235
236        let v = unsafe { sys::lzc_rename(source.as_ref().as_ptr(), target.as_ref().as_ptr()) };
237        if v != 0 {
238            Err(io::Error::from_raw_os_error(v))
239        } else {
240            Ok(())
241        }
242    }
243
244    /// Destroy the given dataset (which may be a filesystem, snapshot, bookmark, volume, etc)
245    ///
246    /// Corresponds to `lzc_destroy()`
247    #[doc(alias = "lzc_destroy")]
248    pub fn destroy<S: CStrArgument>(&self, name: S) -> io::Result<()> {
249        let name = name.into_cstr();
250        let v = unsafe { sys::lzc_destroy(name.as_ref().as_ptr()) };
251
252        if v != 0 {
253            Err(io::Error::from_raw_os_error(v))
254        } else {
255            Ok(())
256        }
257    }
258
259    /// Create snapshot(s)
260    ///
261    /// The snapshots must be from the same pool, and must not reference the same dataset (iow:
262    /// cannot create 2 snapshots of the same filesystem).
263    ///
264    /// Corresponds to `lzc_snapshot()`.
265    #[doc(alias = "lzc_snapshot")]
266    #[doc(alias = "snapshot_raw")]
267    pub fn snapshot<I: IntoIterator<Item = S>, S: CStrArgument>(
268        &self,
269        snaps: I,
270    ) -> Result<(), Error> {
271        let mut arg = NvList::new();
272
273        for i in snaps {
274            arg.insert(i.into_cstr().as_ref(), &()).unwrap();
275        }
276
277        let props = NvList::new();
278        match self.snapshot_raw(&arg, &props) {
279            Ok(()) => Ok(()),
280            Err(Ok(v)) => Err(Error::Io { source: v }),
281            Err(Err(v)) => Err(Error::List { source: v.into() }),
282        }
283    }
284
285    /// Create snapshot(s). `snaps` is a list of `bool` (not `boolean_value`) entries, the names of
286    /// which correspond to snapshot names.
287    ///
288    /// The snapshots must be from the same pool, and must not reference the same dataset (iow:
289    /// cannot create 2 snapshots of the same filesystem with a single call).
290    ///
291    /// Corresponds to `lzc_snapshot()`.
292    // TODO: this is a fairly raw interface, consider abstracting (or at least adding some
293    // restrictions on the NvLists).
294    #[doc(alias = "lzc_snapshot")]
295    pub fn snapshot_raw(
296        &self,
297        snaps: &NvList,
298        props: &NvList,
299    ) -> Result<(), Result<io::Error, NvList>> {
300        let mut nv = ptr::null_mut();
301        let v = unsafe {
302            sys::lzc_snapshot(snaps.as_ptr() as *mut _, props.as_ptr() as *mut _, &mut nv)
303        };
304
305        if v != 0 {
306            if nv.is_null() {
307                Err(Ok(io::Error::from_raw_os_error(v)))
308            } else {
309                Err(Err(unsafe { NvList::from_ptr(nv) }))
310            }
311        } else {
312            Ok(())
313        }
314    }
315
316    #[doc(alias = "destroy_snaps_raw")]
317    #[doc(alias = "lzc_destroy_snaps")]
318    pub fn destroy_snaps<I: IntoIterator<Item = S>, S: CStrArgument>(
319        &self,
320        snaps: I,
321        defer: Defer,
322    ) -> Result<(), (io::Error, NvList)> {
323        let mut snaps_nv = NvList::new();
324
325        for snap in snaps {
326            snaps_nv.insert(snap, &()).unwrap();
327        }
328
329        self.destroy_snaps_raw(&snaps_nv, defer)
330    }
331
332    /// Corresponds to `lzc_destroy_snaps()`
333    #[doc(alias = "lzc_destroy_snaps")]
334    pub fn destroy_snaps_raw(
335        &self,
336        snaps: &NvList,
337        defer: Defer,
338    ) -> Result<(), (io::Error, NvList)> {
339        let mut nv = ptr::null_mut();
340        let v = unsafe {
341            sys::lzc_destroy_snaps(
342                snaps.as_ptr() as *mut _,
343                bool::from(defer) as sys::boolean_t::Type,
344                &mut nv,
345            )
346        };
347
348        if v != 0 {
349            Err((io::Error::from_raw_os_error(v), unsafe {
350                NvList::from_ptr(nv)
351            }))
352        } else {
353            Ok(())
354        }
355    }
356
357    /// Corresponds to `lzc_snaprange_space()`
358    #[doc(alias = "lzc_snaprange_space")]
359    pub fn snaprange_space<F: CStrArgument, L: CStrArgument>(
360        &self,
361        first_snap: F,
362        last_snap: L,
363    ) -> io::Result<u64> {
364        let first_snap = first_snap.into_cstr();
365        let last_snap = last_snap.into_cstr();
366
367        let mut out = 0;
368        let v = unsafe {
369            sys::lzc_snaprange_space(
370                first_snap.as_ref().as_ptr(),
371                last_snap.as_ref().as_ptr(),
372                &mut out,
373            )
374        };
375
376        if v != 0 {
377            Err(io::Error::from_raw_os_error(v))
378        } else {
379            Ok(out)
380        }
381    }
382
383    /// Check if a dataset (a filesystem, or a volume, or a snapshot)
384    /// with the given name exists.
385    ///
386    /// Note: cannot check for bookmarks
387    ///
388    /// Corresponds to `lzc_exists()`.
389    #[doc(alias = "lzc_exists")]
390    pub fn exists<S: CStrArgument>(&self, name: S) -> bool {
391        let name = name.into_cstr();
392        let v = unsafe { sys::lzc_exists(name.as_ref().as_ptr()) };
393        v != 0
394    }
395
396    // 0.8.?
397    /// Corresponds to `lzc_sync()`.
398    #[doc(alias = "lzc_sync")]
399    pub fn sync<S: CStrArgument>(&self, pool_name: S, force: bool) -> io::Result<()> {
400        let pool_name = pool_name.into_cstr();
401        let mut args = NvList::new_unique_names();
402
403        // note: always include for compat with <=2.0.0
404        args.insert("force", &force).unwrap();
405
406        let v = unsafe {
407            sys::lzc_sync(
408                pool_name.as_ref().as_ptr(),
409                args.as_ptr() as *mut _,
410                ptr::null_mut(),
411            )
412        };
413        if v != 0 {
414            Err(io::Error::from_raw_os_error(v))
415        } else {
416            Ok(())
417        }
418    }
419
420    /// Create "user holds" on snapshots.  If there is a hold on a snapshot,
421    /// the snapshot can not be destroyed.  (However, it can be marked for deletion
422    /// by lzc_destroy_snaps(defer=B_TRUE).)                               
423    ///                                           
424    /// The keys in the nvlist are snapshot names.                     
425    /// The snapshots must all be in the same pool.
426    /// The value is the name of the hold (string type).
427    #[doc(alias = "lzc_hold")]
428    pub fn hold_raw(
429        &self,
430        holds: &NvListRef,
431        cleanup_fd: Option<RawFd>,
432    ) -> Result<(), Result<io::Error, NvList>> {
433        let mut errs = ptr::null_mut();
434        let v = unsafe {
435            sys::lzc_hold(
436                holds.as_ptr() as *mut _,
437                cleanup_fd.unwrap_or(-1),
438                &mut errs,
439            )
440        };
441        if v != 0 {
442            // if we have an error list, the return value error is just one of the errors in the
443            // list.
444            if errs.is_null() {
445                Err(Ok(io::Error::from_raw_os_error(v)))
446            } else {
447                Err(Err(unsafe { NvList::from_ptr(errs) }))
448            }
449        } else {
450            Ok(())
451        }
452    }
453
454    /// Create a set of holds, each on a given snapshot
455    ///
456    /// Related: [`get_holds`], [`release`], [`hold_raw`], [`release_raw`].
457    ///
458    /// Corresponds to `lzc_hold`.
459    #[doc(alias = "lzc_hold")]
460    pub fn hold<'a, H, S, N>(&self, holds: H, cleanup_fd: Option<RawFd>) -> Result<(), Error>
461    where
462        H: IntoIterator<Item = &'a (S, N)>,
463        S: 'a + CStrArgument + Clone,
464        N: 'a + CStrArgument + Clone,
465    {
466        let mut holds_nv = NvList::new();
467
468        for he in holds {
469            let ds = he.0.clone().into_cstr();
470            let name = he.1.clone().into_cstr();
471            holds_nv.insert(ds.as_ref(), name.as_ref()).unwrap();
472        }
473
474        match self.hold_raw(&holds_nv, cleanup_fd) {
475            Ok(()) => Ok(()),
476            Err(Ok(v)) => Err(Error::Io { source: v }),
477            Err(Err(v)) => Err(Error::List { source: v.into() }),
478        }
479    }
480
481    /// Release holds from various snapshots
482    ///
483    /// The holds nvlist is `[(snap_name, [hold_names])]`, allowing multiple holds for multiple
484    /// snapshots to be released with one call.
485    ///
486    /// Related: [`release`]
487    ///
488    /// Corresponds to `lzc_release`.
489    #[doc(alias = "lzc_release")]
490    pub fn release_raw(&self, holds: &NvListRef) -> Result<(), Result<io::Error, NvList>> {
491        let mut errs = ptr::null_mut();
492        let v = unsafe { sys::lzc_release(holds.as_ptr() as *mut _, &mut errs) };
493        if v != 0 {
494            if errs.is_null() {
495                Err(Ok(io::Error::from_raw_os_error(v)))
496            } else {
497                Err(Err(unsafe { NvList::from_ptr(errs) }))
498            }
499        } else {
500            Ok(())
501        }
502    }
503
504    /// For a list of datasets, release one or more holds by name
505    ///
506    /// Corresponds to `lzc_release`.
507    #[doc(alias = "lzc_release")]
508    pub fn release<'a, F, C, H, N>(&self, holds: F) -> Result<(), Error>
509    where
510        F: IntoIterator<Item = &'a (C, H)>,
511        C: 'a + CStrArgument + Clone,
512        H: 'a + IntoIterator<Item = N> + Clone,
513        N: 'a + CStrArgument + Clone,
514    {
515        let mut r_nv = NvList::new();
516
517        for hi in holds {
518            let mut hold_nv = NvList::new();
519
520            for hold_name in hi.1.clone() {
521                hold_nv.insert(hold_name, &()).unwrap();
522            }
523
524            r_nv.insert(hi.0.clone(), hold_nv.as_ref()).unwrap();
525        }
526
527        match self.release_raw(&r_nv) {
528            Ok(()) => Ok(()),
529            Err(Ok(v)) => Err(Error::Io { source: v }),
530            Err(Err(v)) => Err(Error::List { source: v.into() }),
531        }
532    }
533
534    /// Get the holds for a given snapshot
535    ///
536    /// The returned nvlist is `[(hold_name: String, unix_timestamp_seconds: u64)]`, where the unix
537    /// timestamp is when the hold was created.
538    ///
539    /// Corresponds to `lzc_get_holds()`
540    #[doc(alias = "lzc_get_holds")]
541    pub fn get_holds<S: CStrArgument>(&self, snapname: S) -> io::Result<HoldList> {
542        let snapname = snapname.into_cstr();
543        let mut holds = ptr::null_mut();
544        let v = unsafe { sys::lzc_get_holds(snapname.as_ref().as_ptr(), &mut holds) };
545        if v != 0 {
546            Err(io::Error::from_raw_os_error(v))
547        } else {
548            Ok(HoldList::new(unsafe { NvList::from_ptr(holds) }))
549        }
550    }
551
552    /// Send the described stream
553    ///
554    /// Internally, is a wrapper around [`send_resume_redacted()`]
555    ///
556    /// Corresponds to `lzc_send()`
557    #[doc(alias = "lzc_send")]
558    pub fn send<S: CStrArgument, F: CStrArgument>(
559        &self,
560        snapname: S,
561        from: Option<F>,
562        fd: RawFd,
563        flags: SendFlags,
564    ) -> io::Result<()> {
565        let snapname = snapname.into_cstr();
566        let from = from.map(|a| a.into_cstr());
567
568        let v = unsafe {
569            sys::lzc_send(
570                snapname.as_ref().as_ptr(),
571                from.map_or(ptr::null(), |x| x.as_ref().as_ptr()),
572                fd,
573                flags.into(),
574            )
575        };
576        if v != 0 {
577            Err(io::Error::from_raw_os_error(v))
578        } else {
579            Ok(())
580        }
581    }
582
583    /// Send the described redacted stream
584    ///
585    /// Internally, is a wrapper around [`send_resume_redacted()`]
586    ///
587    /// Corresponds to `lzc_send_redacted()`
588    #[doc(alias = "lzc_send_redacted")]
589    #[cfg(features = "v2_00")]
590    pub fn send_redacted<S: CStrArgument, F: CStrArgument, R: CStrArgument>(
591        &self,
592        snapname: S,
593        from: F,
594        fd: RawFd,
595        redactbook: R,
596        flags: SendFlags,
597    ) -> io::Result<()> {
598        let snapname = snapname.into_cstr();
599        let from = from.into_cstr();
600        let redactbook = redactbook.into_cstr();
601
602        let v = unsafe {
603            sys::lzc_send_redacted(
604                snapname.as_ref().as_ptr(),
605                from.as_ref().as_ptr(),
606                fd,
607                redactbook.as_ref().as_ptr(),
608                flags.into(),
609            )
610        };
611        if v != 0 {
612            Err(io::Error::from_raw_os_error(v))
613        } else {
614            Ok(())
615        }
616    }
617
618    /// Send the described stream with resume information
619    ///
620    /// Internally, this is a wrapper around [`send_resume_redacted()`].
621    ///
622    /// Corresponds to `lzc_send_resume()`
623    #[doc(alias = "lzc_send_resume")]
624    pub fn send_resume<S: CStrArgument, F: CStrArgument>(
625        &self,
626        snapname: S,
627        from: F,
628        fd: RawFd,
629        flags: SendFlags,
630        resume_obj: u64,
631        resume_off: u64,
632    ) -> io::Result<()> {
633        let snapname = snapname.into_cstr();
634        let from = from.into_cstr();
635
636        let v = unsafe {
637            sys::lzc_send_resume(
638                snapname.as_ref().as_ptr(),
639                from.as_ref().as_ptr(),
640                fd,
641                flags.into(),
642                resume_obj,
643                resume_off,
644            )
645        };
646        if v != 0 {
647            Err(io::Error::from_raw_os_error(v))
648        } else {
649            Ok(())
650        }
651    }
652
653    /// Send the described stream with resume and redact info
654    ///
655    /// Corresponds to `lzc_send_resume_redacted()`
656    #[doc(alias = "lzc_send_resume_redacted")]
657    #[cfg(features = "v2_00")]
658    pub fn send_resume_redacted<S: CStrArgument, F: CStrArgument, R: CStrArgument>(
659        &self,
660        snapname: S,
661        from: F,
662        fd: RawFd,
663        flags: SendFlags,
664        resume_obj: u64,
665        resume_off: u64,
666        redactbook: R,
667    ) -> io::Result<()> {
668        let snapname = snapname.into_cstr();
669        let from = from.into_cstr();
670        let redactbook = redactbook.into_cstr();
671
672        let r = unsafe {
673            sys::lzc_send_resume_redacted(
674                snapname.as_ref().as_ptr(),
675                from.as_ref().as_ptr(),
676                fd,
677                flags.into(),
678                resume_obj,
679                resume_off,
680                redactbook.as_ref().as_ptr(),
681            )
682        };
683
684        if r != 0 {
685            Err(io::Error::from_raw_os_error(r))
686        } else {
687            Ok(())
688        }
689    }
690
691    /// Estimate the size of a send stream
692    ///
693    /// Corresponds to `lzc_send_space_resume_redacted()`
694    // FIXME: many parameters should probably be `Option<T>`
695    // TODO: consider passing arguments here as a struct so we can use names
696    #[doc(alias = "lzc_send_space_resume_redacted")]
697    #[cfg(features = "v2_00")]
698    pub fn send_space_resume_redacted<S: CStrArgument, F: CStrArgument, R: CStrArgument>(
699        &self,
700        snapname: S,
701        from: F,
702        flags: SendFlags,
703        resume_obj: u64,
704        resume_off: u64,
705        resume_bytes: u64,
706        redactbook: R,
707        fd: RawFd,
708    ) -> io::Result<u64> {
709        let snapname = snapname.into_cstr();
710        let from = from.into_cstr();
711
712        let mut space = 0;
713
714        let r = unsafe {
715            sys::lzc_send_space_resume_redacted(
716                snapname.as_ref().as_ptr(),
717                from.as_ref().as_ptr(),
718                flags.into(),
719                resume_obj,
720                resume_off,
721                resume_bytes,
722                redactbook,
723                fd,
724                &mut space,
725            )
726        };
727
728        if r != 0 {
729            Err(io::Error::from_raw_os_error(r))
730        } else {
731            Ok(space)
732        }
733    }
734
735    /// Estimate the size of the stream to be sent if [`send`] were called with the same arguments
736    ///
737    /// Internally, this is a wrapper around [`send_space_resume_redacted()`].
738    ///
739    /// Corresponds to `lzc_send_space()`
740    #[doc(alias = "lzc_send_space")]
741    pub fn send_space<S: CStrArgument, F: CStrArgument>(
742        &self,
743        snapname: S,
744        from: F,
745        flags: SendFlags,
746    ) -> io::Result<u64> {
747        let snapname = snapname.into_cstr();
748        let from = from.into_cstr();
749
750        let mut space = 0;
751        let r = unsafe {
752            sys::lzc_send_space(
753                snapname.as_ref().as_ptr(),
754                from.as_ref().as_ptr(),
755                flags.into(),
756                &mut space,
757            )
758        };
759
760        if r != 0 {
761            Err(io::Error::from_raw_os_error(r))
762        } else {
763            Ok(space)
764        }
765    }
766
767    /// Corresponds to `lzc_receive()`
768    #[doc(alias = "lzc_receive")]
769    pub fn receive<S: CStrArgument, O: CStrArgument>(
770        &self,
771        snapname: S,
772        props: Option<&NvListRef>,
773        origin: Option<O>,
774        force: bool,
775        raw: bool,
776        fd: RawFd,
777    ) -> io::Result<()> {
778        let snapname = snapname.into_cstr();
779        let origin = origin.map(|x| x.into_cstr());
780
781        let r = unsafe {
782            sys::lzc_receive(
783                snapname.as_ref().as_ptr(),
784                props.map_or(ptr::null_mut(), |x| x.as_ptr() as *mut _),
785                origin.map_or(ptr::null(), |x| x.as_ref().as_ptr()),
786                if force { 1 } else { 0 },
787                if raw { 1 } else { 0 },
788                fd,
789            )
790        };
791
792        if r != 0 {
793            Err(io::Error::from_raw_os_error(r))
794        } else {
795            Ok(())
796        }
797    }
798
799    /// Corresponds to `lzc_receive_resumable()`
800    // internally, only a flag differs from `recv`
801    // consider implimenting something that takes `resumeable` as a flag
802    #[doc(alias = "lzc_receive_resumable")]
803    pub fn receive_resumable<S: CStrArgument, O: CStrArgument>(
804        &self,
805        snapname: S,
806        props: &NvListRef,
807        origin: O,
808        force: bool,
809        raw: bool,
810        fd: RawFd,
811    ) -> io::Result<()> {
812        let snapname = snapname.into_cstr();
813        let origin = origin.into_cstr();
814
815        let r = unsafe {
816            sys::lzc_receive_resumable(
817                snapname.as_ref().as_ptr(),
818                props.as_ptr() as *mut _,
819                origin.as_ref().as_ptr(),
820                if force { 1 } else { 0 },
821                if raw { 1 } else { 0 },
822                fd,
823            )
824        };
825
826        if r != 0 {
827            Err(io::Error::from_raw_os_error(r))
828        } else {
829            Ok(())
830        }
831    }
832
833    /*
834    pub fn receive_with_header<S: CStrArgument, O: CStrArgument>(&self, snapname: S, props: &NvListRef, origin: O, force: bool, resumeable: bool, raw: bool, fd: RawFd, begin_record: &DmuReplayRecordRef) -> io::Result<()> {
835        unimplemented!()
836    }
837    */
838
839    /*
840    pub fn receive_one<S: CStrArgument, O: CStrArgument>(&self, snapname: S, cmdprops: &NvListRef, wkey: Option<&[u8]>, origin: O, force: bool, resumeable: bool, raw: bool, input_fd: RawFd, begin_record: &DmuReplayRecordRef) -> io::Result<(/* bytes */ u64, /* errflags */u64, /* errors */ NvList)> {
841        unimplemented!()
842    }
843    */
844
845    /*
846    pub fn receive_with_cmdprops<S: CStrArgument, O: CStrArgument>(&self, snapname: S, cmdprops: &NvListRef, wkey: Option<&[u8]>, origin: O, force: bool, resumeable: bool, raw: bool, input_fd: RawFd, begin_record: &DmuReplayRecordRef) -> io::Result<(/* bytes */ u64, /* errflags */u64, /* errors */ NvList)> {
847        unimplemented!()
848    }
849    */
850
851    /// Corresponds to `lzc_rollback()`
852    #[doc(alias = "lzc_rollback")]
853    pub fn rollback<S: CStrArgument>(&self, fsname: S) -> io::Result<std::ffi::CString> {
854        let fsname = fsname.into_cstr();
855        let mut rname = vec![0u8; sys::ZFS_MAX_DATASET_NAME_LEN as usize + 1];
856
857        let r = unsafe {
858            sys::lzc_rollback(
859                fsname.as_ref().as_ptr(),
860                rname.as_mut_ptr() as *mut std::os::raw::c_char,
861                rname.len() as std::os::raw::c_int,
862            )
863        };
864
865        if r != 0 {
866            Err(io::Error::from_raw_os_error(r))
867        } else {
868            let p = rname.iter().position(|x| *x == b'\0').unwrap();
869            rname.resize(p, 0);
870            Ok(std::ffi::CString::new(rname).unwrap())
871        }
872    }
873
874    /// Corresponds to `lzc_rollback_to()`
875    #[doc(alias = "lzc_rollback_to")]
876    pub fn rollback_to<F: CStrArgument, S: CStrArgument>(
877        &self,
878        fsname: F,
879        snapname: S,
880    ) -> io::Result<()> {
881        let fsname = fsname.into_cstr();
882        let snapname = snapname.into_cstr();
883
884        let r =
885            unsafe { sys::lzc_rollback_to(fsname.as_ref().as_ptr(), snapname.as_ref().as_ptr()) };
886
887        if r != 0 {
888            Err(io::Error::from_raw_os_error(r))
889        } else {
890            Ok(())
891        }
892    }
893
894    /// Create bookmarks from existing snapshot or bookmark
895    #[doc(alias = "lzc_bookmark")]
896    pub fn bookmark<I: IntoIterator<Item = (D, S)>, D: CStrArgument, S: CStrArgument>(
897        &self,
898        bookmarks: I,
899    ) -> Result<(), ErrorList> {
900        let mut bookmarks_nv = NvList::new();
901
902        for (new_bm, src) in bookmarks {
903            let src = src.into_cstr();
904            bookmarks_nv.insert(new_bm, src.as_ref()).unwrap();
905        }
906
907        match self.bookmark_raw(&bookmarks_nv) {
908            Ok(()) => Ok(()),
909            Err((_, err_nv)) => Err(ErrorList::from(err_nv)),
910        }
911    }
912
913    /// Create bookmarks from existing snapshot or bookmark
914    ///
915    /// The `bookmarks` nvlist is `[(full_name_of_new_bookmark,
916    /// full_name_of_source_snap_or_bookmark)]`.
917    ///
918    /// Corresponds to `lzc_bookmark()`
919    #[doc(alias = "lzc_bookmark")]
920    pub fn bookmark_raw(&self, bookmarks: &NvListRef) -> Result<(), (io::Error, NvList)> {
921        let mut err = ptr::null_mut();
922        let r = unsafe { sys::lzc_bookmark(bookmarks.as_ptr() as *mut _, &mut err) };
923
924        if r != 0 {
925            Err((io::Error::from_raw_os_error(r), unsafe {
926                NvList::from_ptr(err)
927            }))
928        } else {
929            Ok(())
930        }
931    }
932
933    /// Retreive bookmarks for the given filesystem
934    ///
935    /// `props` is a list of `[(prop_name, ())]`, where `prop_name` names a property on a bookmark.
936    /// All the named properties are returned in the return value as the values of each bookmark.
937    ///
938    /// Corresponds to `lzc_get_bookmarks()`
939    #[doc(alias = "lzc_get_bookmarks")]
940    pub fn get_bookmarks_raw<F: CStrArgument>(
941        &self,
942        fsname: F,
943        props: &NvListRef,
944    ) -> io::Result<NvList> {
945        let mut res = ptr::null_mut();
946        let fsname = fsname.into_cstr();
947
948        let r = unsafe {
949            sys::lzc_get_bookmarks(fsname.as_ref().as_ptr(), props.as_ptr() as *mut _, &mut res)
950        };
951
952        if r != 0 {
953            Err(io::Error::from_raw_os_error(r))
954        } else {
955            Ok(unsafe { NvList::from_ptr(res) })
956        }
957    }
958
959    /// Corresponds to `lzc_get_bookmark_props()`
960    #[doc(alias = "lzc_get_bookmark_props")]
961    pub fn get_bookmark_props<B: CStrArgument>(&self, bookmark: B) -> io::Result<NvList> {
962        let mut res = ptr::null_mut();
963        let bookmark = bookmark.into_cstr();
964
965        let r = unsafe { sys::lzc_get_bookmark_props(bookmark.as_ref().as_ptr(), &mut res) };
966
967        if r != 0 {
968            Err(io::Error::from_raw_os_error(r))
969        } else {
970            Ok(unsafe { NvList::from_ptr(res) })
971        }
972    }
973
974    /// Corresponds to `lzc_destroy_bookmarks()`
975    #[doc(alias = "lzc_destroy_bookmarks")]
976    pub fn destroy_bookmarks(&self, bookmarks: &NvListRef) -> Result<(), (io::Error, NvList)> {
977        let mut errs = ptr::null_mut();
978
979        let r = unsafe { sys::lzc_destroy_bookmarks(bookmarks.as_ptr() as *mut _, &mut errs) };
980
981        if r != 0 {
982            Err((io::Error::from_raw_os_error(r), unsafe {
983                NvList::from_ptr(errs)
984            }))
985        } else {
986            Ok(())
987        }
988    }
989
990    /// Execute a channel program
991    ///
992    /// root privlidges are required to execute a channel program
993    ///
994    /// Corresponds to `lzc_channel_program()`
995    // 0.8.?
996    #[doc(alias = "lzc_channel_program")]
997    pub fn channel_program<P: CStrArgument, R: CStrArgument>(
998        &self,
999        pool: P,
1000        program: R,
1001        instruction_limit: u64,
1002        memlimit: u64,
1003        args: &NvListRef,
1004    ) -> io::Result<NvList> {
1005        let mut out_nv = ptr::null_mut();
1006
1007        let pool = pool.into_cstr();
1008        let program = program.into_cstr();
1009
1010        let r = unsafe {
1011            sys::lzc_channel_program(
1012                pool.as_ref().as_ptr(),
1013                program.as_ref().as_ptr(),
1014                instruction_limit,
1015                memlimit,
1016                args.as_ptr() as *mut _,
1017                &mut out_nv,
1018            )
1019        };
1020
1021        if r != 0 {
1022            Err(io::Error::from_raw_os_error(r))
1023        } else {
1024            Ok(unsafe { NvList::from_ptr(out_nv) })
1025        }
1026    }
1027
1028    /// Execute a read-only channel program
1029    ///
1030    /// root privlidges are required to execute a channel program (even a read-only one)
1031    ///
1032    /// Corresponds to `lzc_channel_program_nosync()`
1033    #[doc(alias = "lzc_channel_program_nosync")]
1034    pub fn channel_program_nosync<P: CStrArgument, R: CStrArgument>(
1035        &self,
1036        pool: P,
1037        program: R,
1038        instruction_limit: u64,
1039        memlimit: u64,
1040        args: &NvListRef,
1041    ) -> io::Result<NvList> {
1042        let mut out_nv = ptr::null_mut();
1043
1044        let pool = pool.into_cstr();
1045        let program = program.into_cstr();
1046
1047        let r = unsafe {
1048            sys::lzc_channel_program_nosync(
1049                pool.as_ref().as_ptr(),
1050                program.as_ref().as_ptr(),
1051                instruction_limit,
1052                memlimit,
1053                args.as_ptr() as *mut _,
1054                &mut out_nv,
1055            )
1056        };
1057
1058        if r != 0 {
1059            Err(io::Error::from_raw_os_error(r))
1060        } else {
1061            Ok(unsafe { NvList::from_ptr(out_nv) })
1062        }
1063    }
1064
1065    /// Create a pool checkpoint
1066    ///
1067    /// Corresponds to `lzc_pool_checkpoint()`
1068    ///
1069    // FIXME: libzfs_core.c lists the specific error returns
1070    // 0.8.?
1071    #[doc(alias = "lzc_pool_checkpoint")]
1072    pub fn pool_checkpoint<P: CStrArgument>(&self, pool: P) -> io::Result<()> {
1073        let pool = pool.into_cstr();
1074
1075        let r = unsafe { sys::lzc_pool_checkpoint(pool.as_ref().as_ptr()) };
1076
1077        if r != 0 {
1078            Err(io::Error::from_raw_os_error(r))
1079        } else {
1080            Ok(())
1081        }
1082    }
1083
1084    /// Discard the pool checkpoint
1085    ///
1086    /// Corresponds to `lzc_pool_checkpoint_discard()`
1087    #[doc(alias = "lzc_pool_checkpoint_discard")]
1088    pub fn pool_checkpoint_discard<P: CStrArgument>(&self, pool: P) -> io::Result<()> {
1089        let pool = pool.into_cstr();
1090
1091        let r = unsafe { sys::lzc_pool_checkpoint_discard(pool.as_ref().as_ptr()) };
1092
1093        if r != 0 {
1094            Err(io::Error::from_raw_os_error(r))
1095        } else {
1096            Ok(())
1097        }
1098    }
1099
1100    /// Corresponds to `lzc_load_key()`
1101    #[doc(alias = "lzc_load_key")]
1102    pub fn load_key<F: CStrArgument>(
1103        &self,
1104        fsname: F,
1105        noop: bool,
1106        keydata: &[u8],
1107    ) -> io::Result<()> {
1108        let fsname = fsname.into_cstr();
1109
1110        let r = unsafe {
1111            sys::lzc_load_key(
1112                fsname.as_ref().as_ptr(),
1113                if noop {
1114                    sys::boolean_t::B_TRUE
1115                } else {
1116                    sys::boolean_t::B_FALSE
1117                },
1118                keydata.as_ptr() as *mut _,
1119                keydata.len().try_into().unwrap(),
1120            )
1121        };
1122
1123        if r != 0 {
1124            Err(io::Error::from_raw_os_error(r))
1125        } else {
1126            Ok(())
1127        }
1128    }
1129
1130    /// Corresponds to `lzc_unload_key()`
1131    #[doc(alias = "lzc_unload_key")]
1132    pub fn unload_key<F: CStrArgument>(&self, fsname: F) -> io::Result<()> {
1133        let fsname = fsname.into_cstr();
1134
1135        let r = unsafe { sys::lzc_unload_key(fsname.as_ref().as_ptr()) };
1136
1137        if r != 0 {
1138            Err(io::Error::from_raw_os_error(r))
1139        } else {
1140            Ok(())
1141        }
1142    }
1143
1144    /// Corresponds to `lzc_change_key()`
1145    #[doc(alias = "lzc_change_key")]
1146    pub fn change_key<F: CStrArgument>(
1147        &self,
1148        fsname: F,
1149        crypt_cmd: u64,
1150        props: &NvListRef,
1151        keydata: Option<&[u8]>,
1152    ) -> io::Result<()> {
1153        let fsname = fsname.into_cstr();
1154
1155        let (k, l) = keydata.map_or((ptr::null_mut(), 0), |v| (v.as_ptr() as *mut _, v.len()));
1156        let r = unsafe {
1157            sys::lzc_change_key(
1158                fsname.as_ref().as_ptr(),
1159                crypt_cmd,
1160                props.as_ptr() as *mut _,
1161                k,
1162                l.try_into().unwrap(),
1163            )
1164        };
1165
1166        if r != 0 {
1167            Err(io::Error::from_raw_os_error(r))
1168        } else {
1169            Ok(())
1170        }
1171    }
1172
1173    /// Corresponds to `lzc_reopen()`
1174    // 0.8.0
1175    #[doc(alias = "lzc_reopen")]
1176    pub fn reopen<P: CStrArgument>(&self, pool: P, scrub_restart: bool) -> io::Result<()> {
1177        let pool = pool.into_cstr();
1178
1179        let r = unsafe {
1180            sys::lzc_reopen(
1181                pool.as_ref().as_ptr(),
1182                if scrub_restart {
1183                    sys::boolean_t::B_TRUE
1184                } else {
1185                    sys::boolean_t::B_FALSE
1186                },
1187            )
1188        };
1189
1190        if r != 0 {
1191            Err(io::Error::from_raw_os_error(r))
1192        } else {
1193            Ok(())
1194        }
1195    }
1196
1197    /// Corresponds to `lzc_initialize()`
1198    // 0.8.0
1199    #[doc(alias = "lzc_initialize")]
1200    pub fn initialize<P: CStrArgument>(
1201        &self,
1202        pool: P,
1203        initialize_func: PoolInitializeFunc,
1204        vdevs: &NvListRef,
1205    ) -> Result<(), (io::Error, NvList)> {
1206        let pool = pool.into_cstr();
1207
1208        let mut err_nv = ptr::null_mut();
1209        let r = unsafe {
1210            sys::lzc_initialize(
1211                pool.as_ref().as_ptr(),
1212                initialize_func.as_raw(),
1213                vdevs.as_ptr() as *mut _,
1214                &mut err_nv,
1215            )
1216        };
1217
1218        if r != 0 {
1219            Err((io::Error::from_raw_os_error(r), unsafe {
1220                NvList::from_ptr(err_nv)
1221            }))
1222        } else {
1223            Ok(())
1224        }
1225    }
1226
1227    /// Corresponds to `lzc_trim()`
1228    // 0.8.0
1229    #[doc(alias = "lzc_trim")]
1230    pub fn trim<P: CStrArgument>(
1231        &self,
1232        pool: P,
1233        pool_trim_func: PoolTrimFunc,
1234        rate: u64,
1235        secure: bool,
1236        vdevs: &NvListRef,
1237    ) -> Result<(), (io::Error, NvList)> {
1238        let pool = pool.into_cstr();
1239
1240        let mut err_nv = ptr::null_mut();
1241        let r = unsafe {
1242            sys::lzc_trim(
1243                pool.as_ref().as_ptr(),
1244                pool_trim_func.as_raw(),
1245                rate,
1246                if secure {
1247                    sys::boolean_t::B_TRUE
1248                } else {
1249                    sys::boolean_t::B_FALSE
1250                },
1251                vdevs.as_ptr() as *mut _,
1252                &mut err_nv,
1253            )
1254        };
1255
1256        if r != 0 {
1257            Err((io::Error::from_raw_os_error(r), unsafe {
1258                NvList::from_ptr(err_nv)
1259            }))
1260        } else {
1261            Ok(())
1262        }
1263    }
1264
1265    /// Corresponds to `lzc_redact()`
1266    #[cfg(features = "v2_00")]
1267    #[doc(alias = "lzc_redact")]
1268    pub fn redact<S: CStrArgument, B: CStrArgument>(
1269        &self,
1270        snapname: S,
1271        bookname: B,
1272        snapnv: &NvListRef,
1273    ) -> io::Result<()> {
1274        let snapname = snapname.into_cstr();
1275        let bookname = bookname.into_cstr();
1276
1277        let r = unsafe {
1278            sys::lzc_redact(
1279                snapname.as_ref().as_ptr(),
1280                bookname.as_ref().as_ptr(),
1281                snapnv.as_ptr() as *mut _,
1282            )
1283        };
1284
1285        if r != 0 {
1286            Err(io::Error::from_raw_os_error(r))
1287        } else {
1288            Ok(())
1289        }
1290    }
1291
1292    /// Corresponds to `lzc_wait()`
1293    #[cfg(features = "v2_00")]
1294    #[doc(alias = "lzc_wait")]
1295    pub fn wait<P: CStrArgument>(&self, pool: P, activity: WaitActivity) -> io::Result<bool> {
1296        let pool = pool.into_cstr();
1297
1298        let mut waited = sys::boolean_t::B_FALSE;
1299        let r = unsafe { sys::lzc_wait(pool.as_ref().as_ptr(), activity.as_raw(), &mut waited) };
1300
1301        if r != 0 {
1302            Err(io::Error::from_raw_os_error(r))
1303        } else {
1304            Ok(waited != sys::boolean_t::B_FALSE)
1305        }
1306    }
1307
1308    /// Corresponds to `lzc_wait_tag()`
1309    #[cfg(features = "v2_00")]
1310    #[doc(alias = "lzc_wait_tag")]
1311    pub fn wait_tag<P: CStrArgument>(
1312        &self,
1313        pool: P,
1314        activity: WaitActivity,
1315        tag: u64,
1316    ) -> io::Result<bool> {
1317        let pool = pool.into_cstr();
1318
1319        let mut waited = sys::boolean_t::B_FALSE;
1320        let r = unsafe {
1321            sys::lzc_wait_tag(pool.as_ref().as_ptr(), activity.as_raw(), tag, &mut waited)
1322        };
1323
1324        if r != 0 {
1325            Err(io::Error::from_raw_os_error(r))
1326        } else {
1327            Ok(waited != sys::boolean_t::B_FALSE)
1328        }
1329    }
1330
1331    /// Corresponds to `lzc_wait_fs()`
1332    #[cfg(features = "v2_00")]
1333    #[doc(alias = "lzc_wait_fs")]
1334    pub fn wait_fs<F: CStrArgument>(&self, fs: F, activity: WaitActivity) -> io::Result<bool> {
1335        let fs = fs.into_cstr();
1336
1337        let mut waited = sys::boolean_t::B_FALSE;
1338        let r =
1339            unsafe { sys::lzc_wait_fs(fs.as_ref().as_ptr(), activity.as_raw(), tag, &mut waited) };
1340
1341        if r != 0 {
1342            Err(io::Error::from_raw_os_error(r))
1343        } else {
1344            Ok(waited != sys::boolean_t::B_FALSE)
1345        }
1346    }
1347
1348    /// Corresponds to `lzc_set_bootenv()`
1349    #[cfg(features = "v2_00")]
1350    #[doc(alias = "lzc_set_bootenv")]
1351    pub fn set_bootenv<P: CStrArgument, E: CStrArgument>(
1352        &self,
1353        pool: P,
1354        env: &NvListRef,
1355    ) -> io::Result<()> {
1356        let pool = pool.into_cstr();
1357        let v = unsafe { sys::lzc_set_bootenv(pool.as_ref().as_ptr(), env.as_ptr()) };
1358        if v != 0 {
1359            Err(io::Error::from_raw_os_error(v))
1360        } else {
1361            Ok(())
1362        }
1363    }
1364
1365    /// Corresponds `lzc_get_bootenv()`
1366    #[cfg(features = "v2_00")]
1367    #[doc(alias = "lzc_get_bootenv")]
1368    pub fn get_bootenv<P: CStrArgument>(&self, pool: P) -> io::Result<NvList> {
1369        let pool = pool.into_cstr();
1370        let mut env = ptr::null_mut();
1371        let v = unsafe { sys::lzc_get_bootenv(pool.as_ref().as_ptr(), &mut env) };
1372        if v != 0 {
1373            Err(io::Error::from_raw_os_error(v))
1374        } else {
1375            Ok(unsafe { NvList::from_ptr(env) })
1376        }
1377    }
1378}
1379
1380impl Drop for Zfs {
1381    fn drop(&mut self) {
1382        unsafe { sys::libzfs_core_fini() }
1383    }
1384}
1385
1386#[derive(Debug, PartialEq)]
1387pub enum PoolInitializeFunc {
1388    Start,
1389    Cancel,
1390    Suspend,
1391}
1392
1393impl PoolInitializeFunc {
1394    pub fn as_raw(&self) -> sys::pool_initialize_func_t {
1395        use sys::pool_initialize_func as ifc;
1396        use PoolInitializeFunc::*;
1397
1398        match self {
1399            Start => ifc::POOL_INITIALIZE_START,
1400            Cancel => ifc::POOL_INITIALIZE_CANCEL,
1401            Suspend => ifc::POOL_INITIALIZE_SUSPEND,
1402        }
1403    }
1404}
1405
1406#[derive(Debug, PartialEq)]
1407pub enum PoolTrimFunc {
1408    Start,
1409    Cancel,
1410    Suspend,
1411}
1412
1413impl PoolTrimFunc {
1414    pub fn as_raw(&self) -> sys::pool_trim_func_t {
1415        use sys::pool_trim_func as ptf;
1416        use PoolTrimFunc::*;
1417        match self {
1418            Start => ptf::POOL_TRIM_START,
1419            Cancel => ptf::POOL_TRIM_CANCEL,
1420            Suspend => ptf::POOL_TRIM_SUSPEND,
1421        }
1422    }
1423}
1424
1425#[derive(Debug, PartialEq)]
1426pub enum WaitActivity {
1427    Discard,
1428    Free,
1429    Initialize,
1430    Replace,
1431    Remove,
1432    Resliver,
1433    Scrub,
1434    Trim,
1435}
1436
1437#[derive(Debug, Default, Clone, Copy, PartialEq)]
1438pub struct SendFlags {
1439    pub embed_data: bool,
1440    pub large_block: bool,
1441    pub compress: bool,
1442    pub raw: bool,
1443
1444    #[cfg(features = "v2_00")]
1445    pub saved: bool,
1446}
1447
1448impl From<SendFlags> for u32 {
1449    fn from(sf: SendFlags) -> Self {
1450        let mut f = 0;
1451        if sf.embed_data {
1452            f |= sys::lzc_send_flags::LZC_SEND_FLAG_EMBED_DATA;
1453        }
1454        if sf.large_block {
1455            f |= sys::lzc_send_flags::LZC_SEND_FLAG_LARGE_BLOCK;
1456        }
1457        if sf.compress {
1458            f |= sys::lzc_send_flags::LZC_SEND_FLAG_COMPRESS;
1459        }
1460        if sf.raw {
1461            f |= sys::lzc_send_flags::LZC_SEND_FLAG_RAW;
1462        }
1463        #[cfg(features = "v2_00")]
1464        if sf.saved {
1465            f |= sys::lzc_send_flags::LZC_SEND_FLAG_SAVED;
1466        }
1467
1468        f
1469    }
1470}
1471
1472#[derive(Debug, Clone, Copy, PartialEq)]
1473pub enum Defer {
1474    No,
1475    Yes,
1476}
1477
1478impl Default for Defer {
1479    fn default() -> Self {
1480        Defer::No
1481    }
1482}
1483
1484impl From<Defer> for bool {
1485    fn from(d: Defer) -> Self {
1486        match d {
1487            Defer::No => false,
1488            Defer::Yes => true,
1489        }
1490    }
1491}
1492
1493/// A list of holds for a given snapshot
1494#[derive(Debug)]
1495pub struct HoldList {
1496    nv: NvList,
1497}
1498
1499impl HoldList {
1500    fn new(nv: NvList) -> Self {
1501        Self { nv }
1502    }
1503}
1504
1505impl From<HoldList> for NvList {
1506    fn from(hl: HoldList) -> Self {
1507        hl.nv
1508    }
1509}
1510
1511impl AsRef<NvListRef> for HoldList {
1512    fn as_ref(&self) -> &NvListRef {
1513        &self.nv
1514    }
1515}
1516
1517/// Iterator of holds in the [`HoldList`]
1518#[derive(Debug)]
1519pub struct HoldListIter<'a> {
1520    iter: NvListIter<'a>,
1521}
1522
1523impl<'a> IntoIterator for &'a HoldList {
1524    type Item = (&'a ffi::CStr, std::time::SystemTime);
1525    type IntoIter = HoldListIter<'a>;
1526    fn into_iter(self) -> Self::IntoIter {
1527        HoldListIter {
1528            iter: (&self.nv).into_iter(),
1529        }
1530    }
1531}
1532
1533impl<'a> Iterator for HoldListIter<'a> {
1534    type Item = (&'a ffi::CStr, std::time::SystemTime);
1535
1536    fn next(&mut self) -> Option<Self::Item> {
1537        match self.iter.next() {
1538            Some(nvp) => {
1539                let t = match nvp.data() {
1540                    nvpair::NvData::Uint64(time_sec) => {
1541                        std::time::UNIX_EPOCH + std::time::Duration::from_secs(time_sec)
1542                    }
1543                    v => panic!("unexpected datatype in hold list {:?}", v),
1544                };
1545                Some((nvp.name(), t))
1546            }
1547            None => None,
1548        }
1549    }
1550}