diff --git a/Cargo.lock b/Cargo.lock index e9e5528..3b44b20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "bitflags" @@ -358,7 +358,7 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "zfs-core" -version = "0.5.0" +version = "0.5.1-delphix0" dependencies = [ "cstr-argument", "foreign-types", @@ -373,7 +373,7 @@ dependencies = [ [[package]] name = "zfs-core-sys" -version = "0.5.0" +version = "0.5.1-delphix0" dependencies = [ "build-env", "libc", diff --git a/zfs-core-sys/Cargo.toml b/zfs-core-sys/Cargo.toml index 61064fb..635b17d 100644 --- a/zfs-core-sys/Cargo.toml +++ b/zfs-core-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zfs-core-sys" -version = "0.5.0" +version = "0.5.1-delphix0" authors = ["Cody P Schafer "] include = ["**/*.rs", "Cargo.toml"] documentation = "https://docs.rs/zfs-core-sys" diff --git a/zfs-core-sys/src/bindings.rs b/zfs-core-sys/src/bindings.rs index b427dc6..070d635 100644 --- a/zfs-core-sys/src/bindings.rs +++ b/zfs-core-sys/src/bindings.rs @@ -1978,6 +1978,12 @@ extern "C" { arg2: *mut *mut nvlist_t, ) -> ::std::os::raw::c_int; } +extern "C" { + pub fn lzc_get_snapshot_refs( + arg1: *const ::std::os::raw::c_char, + arg2: *mut *mut nvlist_t, + ) -> ::std::os::raw::c_int; +} pub mod lzc_send_flags { pub type Type = ::std::os::raw::c_uint; pub const LZC_SEND_FLAG_EMBED_DATA: Type = 1; diff --git a/zfs-core/Cargo.toml b/zfs-core/Cargo.toml index a9d9923..66775ad 100644 --- a/zfs-core/Cargo.toml +++ b/zfs-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zfs-core" -version = "0.5.0" +version = "0.5.1-delphix0" authors = ["Cody P Schafer "] include = ["**/*.rs", "Cargo.toml"] documentation = "https://docs.rs/zfs-core" @@ -14,7 +14,7 @@ v2_00 = [] [dependencies] nvpair = { path = "../nvpair", version = "0.5.2-delphix0" } -zfs-core-sys = { path = "../zfs-core-sys", version = "0.5.0" } +zfs-core-sys = { path = "../zfs-core-sys", version = "0.5.1-delphix0" } cstr-argument = "0.1" foreign-types = "0.5.0" rand = "0.8" diff --git a/zfs-core/src/lib.rs b/zfs-core/src/lib.rs index e56222c..f154600 100644 --- a/zfs-core/src/lib.rs +++ b/zfs-core/src/lib.rs @@ -549,6 +549,30 @@ impl Zfs { } } + /// Enumerate snapshots in the pool that owns `fsname` which carry the + /// `DS_FIELD_SNAPSHOT_REFERENCE` ZAP entry written by the layered-receive + /// path. + /// + /// `fsname` may be a pool name or any descendant filesystem name; the + /// kernel walks the entire owning pool. The returned [`SnapshotRefList`] + /// iterates `(snap_name, guid, ref_id)` triples. + /// + /// Corresponds to `lzc_get_snapshot_refs()`. + #[doc(alias = "lzc_get_snapshot_refs")] + pub fn list_snapshot_refs( + &self, + fsname: S, + ) -> io::Result { + let fsname = fsname.into_cstr(); + let mut refs = ptr::null_mut(); + let v = unsafe { sys::lzc_get_snapshot_refs(fsname.as_ref().as_ptr(), &mut refs) }; + if v != 0 { + Err(io::Error::from_raw_os_error(v)) + } else { + Ok(SnapshotRefList::new(unsafe { NvList::from_ptr(refs) })) + } + } + /// Send the described stream /// /// Internally, is a wrapper around [`send_resume_redacted()`] @@ -1548,3 +1572,81 @@ impl<'a> Iterator for HoldListIter<'a> { } } } + +/// A list of snapshots in a pool carrying the `DS_FIELD_SNAPSHOT_REFERENCE` +/// ZAP entry, returned by [`Zfs::list_snapshot_refs`]. +/// +/// The underlying nvlist maps each snapshot name to an inner nvlist with +/// `guid` (u64) and `ref_id` (u64) fields. +#[derive(Debug)] +pub struct SnapshotRefList { + nv: NvList, +} + +impl SnapshotRefList { + fn new(nv: NvList) -> Self { + Self { nv } + } +} + +impl From for NvList { + fn from(l: SnapshotRefList) -> Self { + l.nv + } +} + +impl AsRef for SnapshotRefList { + fn as_ref(&self) -> &NvListRef { + &self.nv + } +} + +/// A single entry in [`SnapshotRefList`]. +#[derive(Debug, Clone, Copy)] +pub struct SnapshotRef<'a> { + /// Snapshot name (`pool/dataset@snap`). + pub name: &'a ffi::CStr, + /// `dsl_dataset_phys(ds)->ds_guid` of the snapshot. + pub guid: u64, + /// Sender-assigned reference id stored in `DS_FIELD_SNAPSHOT_REFERENCE`. + pub ref_id: u64, +} + +/// Iterator of snapshot refs in the [`SnapshotRefList`]. +#[derive(Debug)] +pub struct SnapshotRefListIter<'a> { + iter: NvListIter<'a>, +} + +impl<'a> IntoIterator for &'a SnapshotRefList { + type Item = SnapshotRef<'a>; + type IntoIter = SnapshotRefListIter<'a>; + fn into_iter(self) -> Self::IntoIter { + SnapshotRefListIter { + iter: (&self.nv).into_iter(), + } + } +} + +impl<'a> Iterator for SnapshotRefListIter<'a> { + type Item = SnapshotRef<'a>; + + fn next(&mut self) -> Option { + let nvp = self.iter.next()?; + let inner = match nvp.data() { + nvpair::NvData::NvListRef(l) => l, + v => panic!("unexpected datatype in snapshot ref list {:?}", v), + }; + let guid = inner + .lookup_uint64("guid") + .expect("snapshot ref entry missing 'guid'"); + let ref_id = inner + .lookup_uint64("ref_id") + .expect("snapshot ref entry missing 'ref_id'"); + Some(SnapshotRef { + name: nvp.name(), + guid, + ref_id, + }) + } +}