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#[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
22pub 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#[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 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 panic!("unhandled error type for name {:?}: {:?}", name, data);
137 }
138 }
139 }
140 None => None,
141 }
142 }
143}
144
145impl Zfs {
146 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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 #[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 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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#[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#[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}