use alloc::{string::String, vec::Vec};
use crate::{
block_read::{BlockRead, PoolMember},
error::{Error, Location, Result},
file::with_decoder,
phys::{Dnode, DslDataSet, DslDirectory, EndianOrder},
vdev::Topology,
walk::{read_object_dnode, read_objset, zap_entries, zap_lookup},
};
const MOS_OBJECT_DIRECTORY: u64 = 1;
const ZPL_MASTER_NODE: u64 = 1;
pub(crate) struct Dataset {
pub meta_dnode: Dnode,
}
fn dsl_dir<R: BlockRead>(
members: &mut [PoolMember<R>],
topo: &Topology,
mos: &Dnode,
obj: u64,
order: EndianOrder,
) -> Result<DslDirectory> {
let dnode = read_object_dnode(members, topo, mos, obj, order)?;
with_decoder(dnode.bonus_used(), order, DslDirectory::from_decoder).map_err(|_| {
Error::Inconsistent {
token: "dsl_dir",
where_: Location::Dsl { obj },
}
})
}
pub(crate) fn open_dataset<R: BlockRead>(
members: &mut [PoolMember<R>],
topo: &Topology,
mos: &Dnode,
order: EndianOrder,
names: &[&str],
) -> Result<Dataset> {
let objdir = read_object_dnode(members, topo, mos, MOS_OBJECT_DIRECTORY, order)?;
let mut dir_obj =
zap_lookup(members, topo, &objdir, "root_dataset", order)?.ok_or(Error::NotFound {
component: "root_dataset",
})?;
for name in names {
let dir = dsl_dir(members, topo, mos, dir_obj, order)?;
let child_zap = read_object_dnode(members, topo, mos, dir.child_directory_zap_obj, order)?;
dir_obj = zap_lookup(members, topo, &child_zap, name, order)?.ok_or(Error::NotFound {
component: "dataset",
})?;
}
let dir = dsl_dir(members, topo, mos, dir_obj, order)?;
let ds_obj = dir.head_dataset_obj.ok_or(Error::NotFound {
component: "head_dataset",
})?;
let ds_dnode = read_object_dnode(members, topo, mos, ds_obj, order)?;
let ds =
with_decoder(ds_dnode.bonus_used(), order, DslDataSet::from_decoder).map_err(|_| {
Error::Inconsistent {
token: "dsl_dataset",
where_: Location::Dsl { obj: ds_obj },
}
})?;
let bp = ds.block_pointer.ok_or(Error::Inconsistent {
token: "dataset_no_bp",
where_: Location::Dsl { obj: ds_obj },
})?;
let objset = read_objset(members, topo, &bp, order)?;
Ok(Dataset {
meta_dnode: objset.dnode,
})
}
pub(crate) fn child_dataset_names<R: BlockRead>(
members: &mut [PoolMember<R>],
topo: &Topology,
mos: &Dnode,
order: EndianOrder,
parent: &[&str],
) -> Result<Vec<String>> {
let objdir = read_object_dnode(members, topo, mos, MOS_OBJECT_DIRECTORY, order)?;
let mut dir_obj =
zap_lookup(members, topo, &objdir, "root_dataset", order)?.ok_or(Error::NotFound {
component: "root_dataset",
})?;
for name in parent {
let dir = dsl_dir(members, topo, mos, dir_obj, order)?;
let child_zap = read_object_dnode(members, topo, mos, dir.child_directory_zap_obj, order)?;
dir_obj = zap_lookup(members, topo, &child_zap, name, order)?.ok_or(Error::NotFound {
component: "dataset",
})?;
}
let dir = dsl_dir(members, topo, mos, dir_obj, order)?;
let child_zap = read_object_dnode(members, topo, mos, dir.child_directory_zap_obj, order)?;
let names = zap_entries(members, topo, &child_zap, order)?
.into_iter()
.map(|(name, _obj)| name)
.filter(|name| !name.starts_with('$'))
.collect();
Ok(names)
}
pub(crate) fn root_dir_obj<R: BlockRead>(
members: &mut [PoolMember<R>],
topo: &Topology,
dataset: &Dataset,
order: EndianOrder,
) -> Result<u64> {
let master = read_object_dnode(members, topo, &dataset.meta_dnode, ZPL_MASTER_NODE, order)?;
zap_lookup(members, topo, &master, "ROOT", order)?.ok_or(Error::NotFound { component: "ROOT" })
}
pub(crate) fn list_dir<R: BlockRead>(
members: &mut [PoolMember<R>],
topo: &Topology,
dataset: &Dataset,
dir_obj: u64,
order: EndianOrder,
) -> Result<Vec<(String, u64)>> {
let dir = read_object_dnode(members, topo, &dataset.meta_dnode, dir_obj, order)?;
zap_entries(members, topo, &dir, order)
}
const DIRENT_OBJ_MASK: u64 = (1 << 48) - 1;
pub(crate) fn resolve_path<R: BlockRead>(
members: &mut [PoolMember<R>],
topo: &Topology,
dataset: &Dataset,
order: EndianOrder,
components: &[&str],
) -> Result<(u64, u64)> {
let mut dir_obj = root_dir_obj(members, topo, dataset, order)?;
let mut result = None;
for (i, comp) in components.iter().enumerate() {
let dir = read_object_dnode(members, topo, &dataset.meta_dnode, dir_obj, order)?;
let value = zap_lookup(members, topo, &dir, comp, order)?
.ok_or(Error::NotFound { component: "path" })?;
let obj = value & DIRENT_OBJ_MASK;
if i + 1 == components.len() {
result = Some((obj, value));
} else {
dir_obj = obj; }
}
result.ok_or(Error::NotFound {
component: "empty_path",
})
}
const SA_MAGIC: u32 = 0x002F_505A;
const SA_SIZE_OFFSET_STD: usize = 8;
pub(crate) fn sa_file_size(bonus: &[u8], order: EndianOrder) -> Result<u64> {
let unsupported = Error::UnsupportedFeature("sa_layout");
let magic_bytes = bonus.get(0..4).ok_or(unsupported.clone())?;
let magic = match order {
EndianOrder::Big => u32::from_be_bytes(magic_bytes.try_into().unwrap()),
EndianOrder::Little => u32::from_le_bytes(magic_bytes.try_into().unwrap()),
};
if magic != SA_MAGIC {
return Err(unsupported);
}
let info_bytes = bonus.get(4..6).ok_or(unsupported.clone())?;
let layout_info = match order {
EndianOrder::Big => u16::from_be_bytes(info_bytes.try_into().unwrap()),
EndianOrder::Little => u16::from_le_bytes(info_bytes.try_into().unwrap()),
};
let hdrsz = usize::from((layout_info >> 10) & 0x3f) * 8;
let off = hdrsz + SA_SIZE_OFFSET_STD;
let size_bytes = bonus.get(off..off + 8).ok_or(unsupported)?;
Ok(match order {
EndianOrder::Big => u64::from_be_bytes(size_bytes.try_into().unwrap()),
EndianOrder::Little => u64::from_le_bytes(size_bytes.try_into().unwrap()),
})
}