Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion zfs-core-sys/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "zfs-core-sys"
version = "0.5.0"
version = "0.5.1-delphix0"
authors = ["Cody P Schafer <dev@codyps.com>"]
include = ["**/*.rs", "Cargo.toml"]
documentation = "https://docs.rs/zfs-core-sys"
Expand Down
6 changes: 6 additions & 0 deletions zfs-core-sys/src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions zfs-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "zfs-core"
version = "0.5.0"
version = "0.5.1-delphix0"
authors = ["Cody P Schafer <dev@codyps.com>"]
include = ["**/*.rs", "Cargo.toml"]
documentation = "https://docs.rs/zfs-core"
Expand All @@ -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"
Expand Down
102 changes: 102 additions & 0 deletions zfs-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<S: CStrArgument>(
&self,
fsname: S,
) -> io::Result<SnapshotRefList> {
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()`]
Expand Down Expand Up @@ -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<SnapshotRefList> for NvList {
fn from(l: SnapshotRefList) -> Self {
l.nv
}
}

impl AsRef<NvListRef> 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<Self::Item> {
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,
})
}
}