use core::fmt;
#[cfg(feature = "std")]
use std::error;
use crate::checksum::{label_checksum, label_verify, LabelChecksumError, LabelVerifyError, Sha256};
use crate::phys::{
is_multiple_of_sector_size, ChecksumTail, Compatibility, EndianOrder, FeatureSet,
FeatureSetDecodeError, NvArray, NvDecodeError, NvList, PoolConfigKey, PoolErrata,
PoolErrataDecodeError, PoolState, PoolStateDecodeError, SpaVersion, SpaVersionError, UberBlock,
VdevTreeKey, VdevType, SECTOR_SHIFT,
};
use crate::util::Fstr;
pub struct BootBlock<'a> {
pub payload: &'a [u8],
}
impl BootBlock<'_> {
pub const SIZE: usize = 3584 * 1024;
pub const BLOCK_DEVICE_OFFSET: u64 = 2 * Label::SECTORS;
pub const PAYLOAD_SIZE: usize = Self::SIZE;
pub const SECTORS: u64 = (Self::SIZE >> SECTOR_SHIFT) as u64;
pub fn from_bytes(bytes: &[u8]) -> Result<BootBlock<'_>, BootBlockDecodeError> {
if bytes.len() != Self::SIZE {
return Err(BootBlockDecodeError::InvalidSize { size: bytes.len() });
}
Ok(BootBlock { payload: bytes })
}
pub fn to_bytes(&self, bytes: &mut [u8]) -> Result<(), BootBlockEncodeError> {
if bytes.len() != Self::SIZE {
return Err(BootBlockEncodeError::InvalidSize { size: bytes.len() });
}
if self.payload.len() != Self::PAYLOAD_SIZE {
return Err(BootBlockEncodeError::InvalidPayloadSize {
size: self.payload.len(),
});
}
bytes.copy_from_slice(self.payload);
Ok(())
}
}
#[derive(Debug)]
pub enum BootBlockDecodeError {
InvalidSize {
size: usize,
},
}
impl fmt::Display for BootBlockDecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BootBlockDecodeError::InvalidSize { size } => {
write!(f, "BootBlock decode error, invalid size {size}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for BootBlockDecodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
#[derive(Debug)]
pub enum BootBlockEncodeError {
InvalidPayloadSize {
size: usize,
},
InvalidSize {
size: usize,
},
}
impl fmt::Display for BootBlockEncodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BootBlockEncodeError::InvalidPayloadSize { size } => {
write!(f, "BootBlock encode error, invalid payload size {size}")
}
BootBlockEncodeError::InvalidSize { size } => {
write!(f, "BootBlock encode error, invalid size {size}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for BootBlockEncodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
pub struct LabelBlank<'a> {
pub payload: &'a [u8],
}
impl LabelBlank<'_> {
pub const SIZE: usize = 8 * 1024;
pub const LABEL_OFFSET: u64 = 0;
pub const PAYLOAD_SIZE: usize = Self::SIZE;
pub fn from_bytes(bytes: &[u8]) -> Result<LabelBlank<'_>, LabelBlankDecodeError> {
if bytes.len() != Self::SIZE {
return Err(LabelBlankDecodeError::InvalidSize { size: bytes.len() });
}
Ok(LabelBlank { payload: bytes })
}
pub fn to_bytes(&self, bytes: &mut [u8]) -> Result<(), LabelBlankEncodeError> {
if bytes.len() != Self::SIZE {
return Err(LabelBlankEncodeError::InvalidSize { size: bytes.len() });
}
if self.payload.len() != Self::PAYLOAD_SIZE {
return Err(LabelBlankEncodeError::InvalidPayloadSize {
size: self.payload.len(),
});
}
bytes.copy_from_slice(self.payload);
Ok(())
}
}
#[derive(Debug)]
pub enum LabelBlankDecodeError {
InvalidSize {
size: usize,
},
}
impl fmt::Display for LabelBlankDecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LabelBlankDecodeError::InvalidSize { size } => {
write!(f, "LabelBlank decode error, invalid size {size}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for LabelBlankDecodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
#[derive(Debug)]
pub enum LabelBlankEncodeError {
InvalidPayloadSize {
size: usize,
},
InvalidSize {
size: usize,
},
}
impl fmt::Display for LabelBlankEncodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LabelBlankEncodeError::InvalidPayloadSize { size } => {
write!(f, "LabelBlank encode error, invalid payload size {size}")
}
LabelBlankEncodeError::InvalidSize { size } => {
write!(f, "LabelBlank encode error, invalid size {size}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for LabelBlankEncodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
pub struct LabelBootHeader<'a> {
pub payload: &'a [u8],
}
impl LabelBootHeader<'_> {
pub const SIZE: usize = 8 * 1024;
pub const LABEL_OFFSET: u64 =
LabelBlank::LABEL_OFFSET + (LabelBlank::SIZE >> SECTOR_SHIFT) as u64;
pub const PAYLOAD_SIZE: usize = Self::SIZE - ChecksumTail::SIZE;
pub fn from_bytes<'a>(
bytes: &'a [u8],
offset: u64,
sha256: &mut Sha256,
) -> Result<LabelBootHeader<'a>, LabelBootHeaderDecodeError> {
if bytes.len() != Self::SIZE {
return Err(LabelBootHeaderDecodeError::InvalidSize { size: bytes.len() });
}
label_verify(bytes, offset, sha256)?;
Ok(LabelBootHeader {
payload: &bytes[0..Self::PAYLOAD_SIZE],
})
}
pub fn to_bytes(
&self,
bytes: &mut [u8],
offset: u64,
sha256: &mut Sha256,
order: EndianOrder,
) -> Result<(), LabelBootHeaderEncodeError> {
if bytes.len() != Self::SIZE {
return Err(LabelBootHeaderEncodeError::InvalidSize { size: bytes.len() });
}
if self.payload.len() != Self::PAYLOAD_SIZE {
return Err(LabelBootHeaderEncodeError::InvalidPayloadSize {
size: self.payload.len(),
});
}
bytes[0..Self::PAYLOAD_SIZE].copy_from_slice(self.payload);
label_checksum(bytes, offset, sha256, order)?;
Ok(())
}
pub fn checksum(
bytes: &mut [u8],
offset: u64,
sha256: &mut Sha256,
order: EndianOrder,
) -> Result<(), LabelBootHeaderEncodeError> {
if bytes.len() != Self::SIZE {
return Err(LabelBootHeaderEncodeError::InvalidSize { size: bytes.len() });
}
label_checksum(bytes, offset, sha256, order)?;
Ok(())
}
pub fn verify(
bytes: &[u8],
offset: u64,
sha256: &mut Sha256,
) -> Result<(), LabelBootHeaderDecodeError> {
if bytes.len() != Self::SIZE {
return Err(LabelBootHeaderDecodeError::InvalidSize { size: bytes.len() });
}
label_verify(bytes, offset, sha256)?;
Ok(())
}
}
#[derive(Debug)]
pub enum LabelBootHeaderDecodeError {
InvalidSize {
size: usize,
},
LabelVerify {
err: LabelVerifyError,
},
}
impl From<LabelVerifyError> for LabelBootHeaderDecodeError {
fn from(err: LabelVerifyError) -> Self {
LabelBootHeaderDecodeError::LabelVerify { err }
}
}
impl fmt::Display for LabelBootHeaderDecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LabelBootHeaderDecodeError::InvalidSize { size } => {
write!(f, "LabelBootHeader decode error, invalid size {size}")
}
LabelBootHeaderDecodeError::LabelVerify { err } => {
write!(f, "LabelBootHeader decode error | {err}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for LabelBootHeaderDecodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
LabelBootHeaderDecodeError::LabelVerify { err } => Some(err),
_ => None,
}
}
}
#[derive(Debug)]
pub enum LabelBootHeaderEncodeError {
InvalidPayloadSize {
size: usize,
},
InvalidSize {
size: usize,
},
LabelChecksum {
err: LabelChecksumError,
},
}
impl From<LabelChecksumError> for LabelBootHeaderEncodeError {
fn from(err: LabelChecksumError) -> Self {
LabelBootHeaderEncodeError::LabelChecksum { err }
}
}
impl fmt::Display for LabelBootHeaderEncodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LabelBootHeaderEncodeError::InvalidPayloadSize { size } => {
write!(
f,
"LabelBootHeader encode error, invalid payload size {size}"
)
}
LabelBootHeaderEncodeError::InvalidSize { size } => {
write!(f, "LabelBootHeader encode error, invalid size {size}")
}
LabelBootHeaderEncodeError::LabelChecksum { err } => {
write!(f, "LabelBootHeader encode error | {err}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for LabelBootHeaderEncodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
LabelBootHeaderEncodeError::LabelChecksum { err } => Some(err),
_ => None,
}
}
}
pub struct LabelNvPairs<'a> {
pub payload: &'a [u8],
}
impl LabelNvPairs<'_> {
pub const SIZE: usize = 112 * 1024;
pub const LABEL_OFFSET: u64 =
LabelBootHeader::LABEL_OFFSET + (LabelBootHeader::SIZE >> SECTOR_SHIFT) as u64;
pub const PAYLOAD_SIZE: usize = Self::SIZE - ChecksumTail::SIZE;
pub fn from_bytes<'a>(
bytes: &'a [u8],
offset: u64,
sha256: &mut Sha256,
) -> Result<LabelNvPairs<'a>, LabelNvPairsDecodeError> {
if bytes.len() != Self::SIZE {
return Err(LabelNvPairsDecodeError::InvalidSize { size: bytes.len() });
}
label_verify(bytes, offset, sha256)?;
Ok(LabelNvPairs {
payload: &bytes[0..Self::PAYLOAD_SIZE],
})
}
pub fn to_bytes(
&self,
bytes: &mut [u8],
offset: u64,
sha256: &mut Sha256,
order: EndianOrder,
) -> Result<(), LabelNvPairsEncodeError> {
if bytes.len() != Self::SIZE {
return Err(LabelNvPairsEncodeError::InvalidSize { size: bytes.len() });
}
if self.payload.len() != Self::PAYLOAD_SIZE {
return Err(LabelNvPairsEncodeError::InvalidPayloadSize {
size: self.payload.len(),
});
}
bytes[0..Self::PAYLOAD_SIZE].copy_from_slice(self.payload);
label_checksum(bytes, offset, sha256, order)?;
Ok(())
}
pub fn checksum(
bytes: &mut [u8],
offset: u64,
sha256: &mut Sha256,
order: EndianOrder,
) -> Result<(), LabelNvPairsEncodeError> {
if bytes.len() != Self::SIZE {
return Err(LabelNvPairsEncodeError::InvalidSize { size: bytes.len() });
}
label_checksum(bytes, offset, sha256, order)?;
Ok(())
}
pub fn verify(
bytes: &[u8],
offset: u64,
sha256: &mut Sha256,
) -> Result<(), LabelNvPairsDecodeError> {
if bytes.len() != Self::SIZE {
return Err(LabelNvPairsDecodeError::InvalidSize { size: bytes.len() });
}
label_verify(bytes, offset, sha256)?;
Ok(())
}
}
#[derive(Debug)]
pub enum LabelNvPairsDecodeError {
InvalidSize {
size: usize,
},
LabelVerify {
err: LabelVerifyError,
},
}
impl From<LabelVerifyError> for LabelNvPairsDecodeError {
fn from(err: LabelVerifyError) -> Self {
LabelNvPairsDecodeError::LabelVerify { err }
}
}
impl fmt::Display for LabelNvPairsDecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LabelNvPairsDecodeError::InvalidSize { size } => {
write!(f, "LabelNvPairs decode error, invalid size {size}")
}
LabelNvPairsDecodeError::LabelVerify { err } => {
write!(f, "LabelNvPairs decode error | {err}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for LabelNvPairsDecodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
LabelNvPairsDecodeError::LabelVerify { err } => Some(err),
_ => None,
}
}
}
#[derive(Debug)]
pub enum LabelNvPairsEncodeError {
InvalidPayloadSize {
size: usize,
},
InvalidSize {
size: usize,
},
LabelChecksum {
err: LabelChecksumError,
},
}
impl From<LabelChecksumError> for LabelNvPairsEncodeError {
fn from(err: LabelChecksumError) -> Self {
LabelNvPairsEncodeError::LabelChecksum { err }
}
}
impl fmt::Display for LabelNvPairsEncodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LabelNvPairsEncodeError::InvalidPayloadSize { size } => {
write!(f, "LabelNvPairs encode error, invalid payload size {size}")
}
LabelNvPairsEncodeError::InvalidSize { size } => {
write!(f, "LabelNvPairs encode error, invalid size {size}")
}
LabelNvPairsEncodeError::LabelChecksum { err } => {
write!(f, "LabelNvPairs encode error | {err}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for LabelNvPairsEncodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
LabelNvPairsEncodeError::LabelChecksum { err } => Some(err),
_ => None,
}
}
}
pub struct Label {}
impl Label {
pub const COUNT: usize = 4;
pub const SIZE: usize =
LabelBlank::SIZE + LabelBootHeader::SIZE + LabelNvPairs::SIZE + UberBlock::TOTAL_SIZE;
pub const SECTORS: u64 = (Self::SIZE >> SECTOR_SHIFT) as u64;
pub fn offsets(vdev_sectors: u64) -> Result<[u64; 4], LabelSectorsError> {
debug_assert!(is_multiple_of_sector_size(Label::SIZE));
if vdev_sectors < Label::SECTORS * 4 {
return Err(LabelSectorsError::TooSmall {
sectors: vdev_sectors,
});
}
Ok([
0,
Label::SECTORS,
vdev_sectors - 2 * Label::SECTORS,
vdev_sectors - Label::SECTORS,
])
}
}
#[derive(Debug)]
pub enum LabelSectorsError {
TooSmall {
sectors: u64,
},
}
impl fmt::Display for LabelSectorsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LabelSectorsError::TooSmall { sectors } => {
write!(f, "Not enough sectors for Label {sectors}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for LabelSectorsError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum LabelConfig<'a> {
L2Cache(LabelConfigL2Cache),
Spare(LabelConfigSpare),
Storage(LabelConfigStorage<'a>),
}
impl LabelConfig<'_> {
pub fn from_list<'a>(list: &NvList<'a>) -> Result<LabelConfig<'a>, LabelConfigDecodeError> {
let state_str = PoolConfigKey::State.into();
let state = PoolState::try_from(match list.get_u64(state_str)? {
Some(v) => v,
None => return Err(LabelConfigDecodeError::Missing { name: state_str }),
})?;
match state {
PoolState::L2Cache => Ok(LabelConfig::L2Cache(LabelConfigL2Cache::from_list(list)?)),
PoolState::Spare => Ok(LabelConfig::Spare(LabelConfigSpare::from_list(list)?)),
PoolState::Active | PoolState::Exported | PoolState::Destroyed => {
Ok(LabelConfig::Storage(LabelConfigStorage::from_list(list)?))
}
}
}
pub fn state(&self) -> PoolState {
match self {
LabelConfig::L2Cache(_) => PoolState::L2Cache,
LabelConfig::Spare(_) => PoolState::Spare,
LabelConfig::Storage(storage) => storage.state,
}
}
}
#[derive(Debug)]
pub struct LabelConfigL2Cache {
pub allocate_shift: u64,
pub guid: u64,
pub version: SpaVersion,
}
impl LabelConfigL2Cache {
const EXPECTED: [PoolConfigKey; 4] = [
PoolConfigKey::AllocateShift,
PoolConfigKey::Guid,
PoolConfigKey::State,
PoolConfigKey::Version,
];
pub fn from_list(list: &NvList<'_>) -> Result<LabelConfigL2Cache, LabelConfigDecodeError> {
let state_str = PoolConfigKey::State.into();
let state = PoolState::try_from(match list.get_u64(state_str)? {
Some(v) => v,
None => return Err(LabelConfigDecodeError::Missing { name: state_str }),
})?;
if !matches!(state, PoolState::L2Cache) {
return Err(LabelConfigDecodeError::UnexpectedState { state });
}
let allocate_shift_str = PoolConfigKey::AllocateShift.into();
let allocate_shift = match list.get_u64(allocate_shift_str)? {
Some(v) => v,
None => {
return Err(LabelConfigDecodeError::Missing {
name: allocate_shift_str,
})
}
};
let guid_str = PoolConfigKey::Guid.into();
let guid = match list.get_u64(guid_str)? {
Some(v) => v,
None => return Err(LabelConfigDecodeError::Missing { name: guid_str }),
};
let version_str = PoolConfigKey::Version.into();
let version = SpaVersion::try_from(match list.get_u64(version_str)? {
Some(v) => v,
None => return Err(LabelConfigDecodeError::Missing { name: version_str }),
})?;
for pair_res in list {
let pair = pair_res?;
let pool_nv_name = match PoolConfigKey::try_from(pair.name) {
Ok(v) => v,
Err(_) => {
return Err(LabelConfigDecodeError::UnknownField {
name: pair.name.into(),
})
}
};
if !LabelConfigL2Cache::EXPECTED.contains(&pool_nv_name) {
return Err(LabelConfigDecodeError::UnexpectedField { name: pool_nv_name });
}
}
Ok(LabelConfigL2Cache {
allocate_shift,
guid,
version,
})
}
}
#[derive(Debug)]
pub struct LabelConfigSpare {
pub guid: u64,
pub version: SpaVersion,
}
impl LabelConfigSpare {
const EXPECTED: [PoolConfigKey; 3] = [
PoolConfigKey::Guid,
PoolConfigKey::State,
PoolConfigKey::Version,
];
pub fn from_list(list: &NvList<'_>) -> Result<LabelConfigSpare, LabelConfigDecodeError> {
let state_str = PoolConfigKey::State.into();
let state = PoolState::try_from(match list.get_u64(state_str)? {
Some(v) => v,
None => return Err(LabelConfigDecodeError::Missing { name: state_str }),
})?;
if !matches!(state, PoolState::Spare) {
return Err(LabelConfigDecodeError::UnexpectedState { state });
}
let guid_str = PoolConfigKey::Guid.into();
let guid = match list.get_u64(guid_str)? {
Some(v) => v,
None => return Err(LabelConfigDecodeError::Missing { name: guid_str }),
};
let version_str = PoolConfigKey::Version.into();
let version = SpaVersion::try_from(match list.get_u64(version_str)? {
Some(v) => v,
None => return Err(LabelConfigDecodeError::Missing { name: version_str }),
})?;
for pair_res in list {
let pair = pair_res?;
let pool_nv_name = match PoolConfigKey::try_from(pair.name) {
Ok(v) => v,
Err(_) => {
return Err(LabelConfigDecodeError::UnknownField {
name: pair.name.into(),
})
}
};
if !LabelConfigSpare::EXPECTED.contains(&pool_nv_name) {
return Err(LabelConfigDecodeError::UnexpectedField { name: pool_nv_name });
}
}
Ok(LabelConfigSpare { guid, version })
}
}
#[derive(Debug)]
pub struct LabelConfigStorage<'a> {
pub guid: u64,
pub name: &'a str,
pub pool_guid: u64,
pub state: PoolState,
pub top_guid: u64,
pub txg: u64,
pub vdev_tree: LabelVdevTree<'a>,
pub version: SpaVersion,
pub comment: Option<&'a str>,
pub compatibility: Option<Compatibility<'a>>,
pub errata: Option<PoolErrata>,
pub host_id: Option<u64>,
pub host_name: Option<&'a str>,
pub is_log: Option<bool>,
pub is_spare: Option<bool>,
pub features_for_read: Option<FeatureSet>,
pub split_guid: Option<u64>,
pub vdev_children: Option<u64>,
}
impl LabelConfigStorage<'_> {
const EXPECTED: [PoolConfigKey; 18] = [
PoolConfigKey::Comment,
PoolConfigKey::Compatibility,
PoolConfigKey::Guid,
PoolConfigKey::Errata,
PoolConfigKey::FeaturesForRead,
PoolConfigKey::HostId,
PoolConfigKey::HostName,
PoolConfigKey::IsLog,
PoolConfigKey::IsSpare,
PoolConfigKey::Name,
PoolConfigKey::PoolGuid,
PoolConfigKey::SplitGuid,
PoolConfigKey::State,
PoolConfigKey::TopGuid,
PoolConfigKey::Txg,
PoolConfigKey::VdevChildren,
PoolConfigKey::VdevTree,
PoolConfigKey::Version,
];
pub fn from_list<'a>(
list: &NvList<'a>,
) -> Result<LabelConfigStorage<'a>, LabelConfigDecodeError> {
let guid_str = PoolConfigKey::Guid.into();
let guid = match list.get_u64(guid_str)? {
Some(v) => v,
None => return Err(LabelConfigDecodeError::Missing { name: guid_str }),
};
let name_str = PoolConfigKey::Name.into();
let name = match list.get_str(name_str)? {
Some(v) => v,
None => return Err(LabelConfigDecodeError::Missing { name: name_str }),
};
let pool_guid_str = PoolConfigKey::PoolGuid.into();
let pool_guid = match list.get_u64(pool_guid_str)? {
Some(v) => v,
None => {
return Err(LabelConfigDecodeError::Missing {
name: pool_guid_str,
})
}
};
let state_str = PoolConfigKey::State.into();
let state = PoolState::try_from(match list.get_u64(state_str)? {
Some(v) => v,
None => return Err(LabelConfigDecodeError::Missing { name: state_str }),
})?;
match state {
PoolState::Active | PoolState::Exported | PoolState::Destroyed => (),
_ => return Err(LabelConfigDecodeError::UnexpectedState { state }),
};
let top_guid_str = PoolConfigKey::TopGuid.into();
let top_guid = match list.get_u64(top_guid_str)? {
Some(v) => v,
None => return Err(LabelConfigDecodeError::Missing { name: top_guid_str }),
};
let txg_str = PoolConfigKey::Txg.into();
let txg = match list.get_u64(txg_str)? {
Some(v) => v,
None => return Err(LabelConfigDecodeError::Missing { name: txg_str }),
};
let vdev_tree_str = PoolConfigKey::VdevTree.into();
let vdev_tree_list = match list.get_nv_list(vdev_tree_str)? {
Some(v) => v,
None => {
return Err(LabelConfigDecodeError::Missing {
name: vdev_tree_str,
})
}
};
let vdev_tree = LabelVdevTree::from_list(&vdev_tree_list)?;
let version_str = PoolConfigKey::Version.into();
let version = SpaVersion::try_from(match list.get_u64(version_str)? {
Some(v) => v,
None => return Err(LabelConfigDecodeError::Missing { name: version_str }),
})?;
let comment = list.get_str(PoolConfigKey::Comment.into())?;
let compatibility = list
.get_str(PoolConfigKey::Compatibility.into())?
.map(Compatibility::from);
let errata = match list.get_u64(PoolConfigKey::Errata.into())? {
Some(v) => Some(PoolErrata::try_from(v)?),
None => None,
};
let host_id = list.get_u64(PoolConfigKey::HostId.into())?;
let host_name = list.get_str(PoolConfigKey::HostName.into())?;
let is_log = list.get_u64(PoolConfigKey::IsLog.into())?.map(|v| v != 0);
let is_spare = list.get_u64(PoolConfigKey::IsSpare.into())?.map(|v| v != 0);
let features_for_read = match list.get_nv_list(PoolConfigKey::FeaturesForRead.into())? {
Some(v) => Some(FeatureSet::from_nv_list(&v)?),
None => None,
};
let split_guid_str = PoolConfigKey::SplitGuid.into();
let split_guid = list.get_u64(split_guid_str)?;
let vdev_children = list.get_u64(PoolConfigKey::VdevChildren.into())?;
for pair_res in list {
let pair = pair_res?;
let pool_nv_name = match PoolConfigKey::try_from(pair.name) {
Ok(v) => v,
Err(_) => {
return Err(LabelConfigDecodeError::UnknownField {
name: pair.name.into(),
})
}
};
if !LabelConfigStorage::EXPECTED.contains(&pool_nv_name) {
return Err(LabelConfigDecodeError::UnexpectedField { name: pool_nv_name });
}
}
Ok(LabelConfigStorage {
guid,
name,
pool_guid,
state,
top_guid,
txg,
vdev_tree,
version,
comment,
compatibility,
errata,
features_for_read,
host_id,
host_name,
is_log,
is_spare,
split_guid,
vdev_children,
})
}
}
#[derive(Debug)]
pub enum LabelConfigDecodeError {
FeatureSet {
err: FeatureSetDecodeError,
},
Missing {
name: &'static str,
},
PoolErrata {
err: PoolErrataDecodeError,
},
PoolState {
err: PoolStateDecodeError,
},
Nv {
err: NvDecodeError,
},
UnexpectedField {
name: PoolConfigKey,
},
UnexpectedState {
state: PoolState,
},
UnknownField {
name: Fstr<16>,
},
VdevTree {
err: LabelVdevTreeDecodeError,
},
Version {
err: SpaVersionError,
},
}
impl From<FeatureSetDecodeError> for LabelConfigDecodeError {
fn from(err: FeatureSetDecodeError) -> Self {
LabelConfigDecodeError::FeatureSet { err }
}
}
impl From<NvDecodeError> for LabelConfigDecodeError {
fn from(err: NvDecodeError) -> Self {
LabelConfigDecodeError::Nv { err }
}
}
impl From<PoolErrataDecodeError> for LabelConfigDecodeError {
fn from(err: PoolErrataDecodeError) -> Self {
LabelConfigDecodeError::PoolErrata { err }
}
}
impl From<PoolStateDecodeError> for LabelConfigDecodeError {
fn from(err: PoolStateDecodeError) -> Self {
LabelConfigDecodeError::PoolState { err }
}
}
impl From<SpaVersionError> for LabelConfigDecodeError {
fn from(err: SpaVersionError) -> Self {
LabelConfigDecodeError::Version { err }
}
}
impl From<LabelVdevTreeDecodeError> for LabelConfigDecodeError {
fn from(err: LabelVdevTreeDecodeError) -> Self {
LabelConfigDecodeError::VdevTree { err }
}
}
impl fmt::Display for LabelConfigDecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LabelConfigDecodeError::FeatureSet { err } => {
write!(f, "LabelConfig decode error | {err}")
}
LabelConfigDecodeError::Missing { name } => {
write!(f, "LabelConfig decode error, missing field '{name}'")
}
LabelConfigDecodeError::Nv { err } => {
write!(f, "LabelConfig decode error | {err}")
}
LabelConfigDecodeError::PoolErrata { err } => {
write!(f, "LabelConfig decode error | {err}")
}
LabelConfigDecodeError::PoolState { err } => {
write!(f, "LabelConfig decode error | {err}")
}
LabelConfigDecodeError::UnexpectedField { name } => {
write!(f, "LabelConfig decode error, unexpected field '{name}'")
}
LabelConfigDecodeError::UnexpectedState { state } => {
write!(f, "LabelConfig decode error, unexpected state '{state}'")
}
LabelConfigDecodeError::UnknownField { name } => {
write!(f, "LabelConfig decode error, unknown field '{name}'")
}
LabelConfigDecodeError::VdevTree { err } => {
write!(f, "LabelConfig decode error | {err}")
}
LabelConfigDecodeError::Version { err } => {
write!(f, "LabelConfig decode error | {err}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for LabelConfigDecodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
LabelConfigDecodeError::FeatureSet { err } => Some(err),
LabelConfigDecodeError::Nv { err } => Some(err),
LabelConfigDecodeError::PoolErrata { err } => Some(err),
LabelConfigDecodeError::PoolState { err } => Some(err),
LabelConfigDecodeError::VdevTree { err } => Some(err),
LabelConfigDecodeError::Version { err } => Some(err),
_ => None,
}
}
}
#[derive(Debug)]
pub enum LabelVdevTreeType<'a> {
Disk(LabelVdevTreeDisk<'a>),
File(LabelVdevTreeFile<'a>),
Mirror(LabelVdevTreeMirror<'a>),
RaidZ(LabelVdevTreeRaidZ<'a>),
}
impl<'a> LabelVdevTreeType<'a> {
pub fn children(&self) -> Option<NvArray<'a, NvList<'a>>> {
match self {
LabelVdevTreeType::Disk(_) | LabelVdevTreeType::File(_) => None,
LabelVdevTreeType::Mirror(mirror) => Some(mirror.children),
LabelVdevTreeType::RaidZ(raidz) => Some(raidz.children),
}
}
}
#[derive(Debug)]
pub struct LabelVdevTree<'a> {
pub allocate_shift: u64,
pub allocate_size: u64,
pub guid: u64,
pub id: u64,
pub metaslab_array: u64,
pub metaslab_shift: u64,
pub vdev_type: LabelVdevTreeType<'a>,
pub create_txg: Option<u64>,
pub is_log: Option<bool>,
}
impl LabelVdevTree<'_> {
const EXPECTED: [VdevTreeKey; 9] = [
VdevTreeKey::AllocateShift,
VdevTreeKey::AllocateSize,
VdevTreeKey::CreateTxg,
VdevTreeKey::Guid,
VdevTreeKey::Id,
VdevTreeKey::IsLog,
VdevTreeKey::MetaSlabArray,
VdevTreeKey::MetaSlabShift,
VdevTreeKey::VdevType,
];
pub fn from_list<'a>(list: &NvList<'a>) -> Result<LabelVdevTree<'a>, LabelVdevTreeDecodeError> {
let allocate_shift_str = VdevTreeKey::AllocateShift.into();
let allocate_shift = match list.get_u64(allocate_shift_str)? {
Some(v) => v,
None => {
return Err(LabelVdevTreeDecodeError::Missing {
name: allocate_shift_str,
})
}
};
let allocate_size_str = VdevTreeKey::AllocateSize.into();
let allocate_size = match list.get_u64(allocate_size_str)? {
Some(v) => v,
None => {
return Err(LabelVdevTreeDecodeError::Missing {
name: allocate_size_str,
})
}
};
let guid_str = VdevTreeKey::Guid.into();
let guid = match list.get_u64(guid_str)? {
Some(v) => v,
None => return Err(LabelVdevTreeDecodeError::Missing { name: guid_str }),
};
let id_str = VdevTreeKey::Id.into();
let id = match list.get_u64(id_str)? {
Some(v) => v,
None => return Err(LabelVdevTreeDecodeError::Missing { name: id_str }),
};
let metaslab_array_str = VdevTreeKey::MetaSlabArray.into();
let metaslab_array = match list.get_u64(metaslab_array_str)? {
Some(v) => v,
None => {
return Err(LabelVdevTreeDecodeError::Missing {
name: metaslab_array_str,
})
}
};
let metaslab_shift_str = VdevTreeKey::MetaSlabShift.into();
let metaslab_shift = match list.get_u64(metaslab_shift_str)? {
Some(v) => v,
None => {
return Err(LabelVdevTreeDecodeError::Missing {
name: metaslab_shift_str,
})
}
};
let create_txg = list.get_u64(VdevTreeKey::CreateTxg.into())?;
let is_log = list.get_u64(VdevTreeKey::IsLog.into())?.map(|v| v != 0);
let vdev_type_str = VdevTreeKey::VdevType.into();
let vdev_type = match list.get_str(vdev_type_str)? {
Some(v) => match VdevType::try_from(v) {
Ok(v) => v,
Err(_) => {
return Err(LabelVdevTreeDecodeError::UnknownVdevType {
vdev_type: v.into(),
})
}
},
None => {
return Err(LabelVdevTreeDecodeError::Missing {
name: vdev_type_str,
})
}
};
let vdev_type_expected_fields: &[VdevTreeKey];
let vdev_type = match vdev_type {
VdevType::Disk => {
vdev_type_expected_fields = &LabelVdevTreeDisk::EXPECTED;
LabelVdevTreeType::Disk(LabelVdevTreeDisk::from_list(list)?)
}
VdevType::File => {
vdev_type_expected_fields = &LabelVdevTreeFile::EXPECTED;
LabelVdevTreeType::File(LabelVdevTreeFile::from_list(list)?)
}
VdevType::Mirror => {
vdev_type_expected_fields = &LabelVdevTreeMirror::EXPECTED;
LabelVdevTreeType::Mirror(LabelVdevTreeMirror::from_list(list)?)
}
VdevType::RaidZ => {
vdev_type_expected_fields = &LabelVdevTreeRaidZ::EXPECTED;
LabelVdevTreeType::RaidZ(LabelVdevTreeRaidZ::from_list(list)?)
}
_ => return Err(LabelVdevTreeDecodeError::UnsupportedVdevType { vdev_type }),
};
for pair_res in list {
let pair = pair_res?;
let pool_nv_name = match VdevTreeKey::try_from(pair.name) {
Ok(v) => v,
Err(_) => {
return Err(LabelVdevTreeDecodeError::Unknown {
name: pair.name.into(),
})
}
};
if !LabelVdevTree::EXPECTED.contains(&pool_nv_name)
&& !vdev_type_expected_fields.contains(&pool_nv_name)
{
return Err(LabelVdevTreeDecodeError::Unexpected { name: pool_nv_name });
}
}
Ok(LabelVdevTree {
allocate_shift,
allocate_size,
guid,
id,
metaslab_array,
metaslab_shift,
vdev_type,
create_txg,
is_log,
})
}
}
#[derive(Debug)]
pub struct LabelVdevTreeDisk<'a> {
pub path: &'a str,
pub dev_id: Option<&'a str>,
pub phys_path: Option<&'a str>,
pub whole_disk: Option<bool>,
}
impl LabelVdevTreeDisk<'_> {
const EXPECTED: [VdevTreeKey; 4] = [
VdevTreeKey::DevId,
VdevTreeKey::Path,
VdevTreeKey::PhysPath,
VdevTreeKey::WholeDisk,
];
pub fn from_list<'a>(
list: &NvList<'a>,
) -> Result<LabelVdevTreeDisk<'a>, LabelVdevTreeDecodeError> {
let path_str = VdevTreeKey::Path.into();
let path = match list.get_str(path_str)? {
Some(v) => v,
None => return Err(LabelVdevTreeDecodeError::Missing { name: path_str }),
};
let dev_id = list.get_str(VdevTreeKey::DevId.into())?;
let phys_path = list.get_str(VdevTreeKey::PhysPath.into())?;
let whole_disk = list.get_u64(VdevTreeKey::WholeDisk.into())?.map(|v| v != 0);
Ok(LabelVdevTreeDisk {
path,
dev_id,
phys_path,
whole_disk,
})
}
}
#[derive(Debug)]
pub struct LabelVdevTreeFile<'a> {
pub path: &'a str,
}
impl LabelVdevTreeFile<'_> {
const EXPECTED: [VdevTreeKey; 1] = [VdevTreeKey::Path];
pub fn from_list<'a>(
list: &NvList<'a>,
) -> Result<LabelVdevTreeFile<'a>, LabelVdevTreeDecodeError> {
let path_str = VdevTreeKey::Path.into();
let path = match list.get_str(path_str)? {
Some(v) => v,
None => return Err(LabelVdevTreeDecodeError::Missing { name: path_str }),
};
Ok(LabelVdevTreeFile { path })
}
}
pub struct LabelVdevTreeMirror<'a> {
pub children: NvArray<'a, NvList<'a>>,
}
impl fmt::Debug for LabelVdevTreeMirror<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LabelVdevTreeMirror")
.field("count", &self.children.len())
.finish()
}
}
impl LabelVdevTreeMirror<'_> {
const EXPECTED: [VdevTreeKey; 1] = [VdevTreeKey::Children];
pub fn from_list<'a>(
list: &NvList<'a>,
) -> Result<LabelVdevTreeMirror<'a>, LabelVdevTreeDecodeError> {
let children_str = VdevTreeKey::Children.into();
let children = match list.get_nv_list_array(children_str)? {
Some(v) => v,
None => return Err(LabelVdevTreeDecodeError::Missing { name: children_str }),
};
Ok(LabelVdevTreeMirror { children })
}
}
pub struct LabelVdevTreeRaidZ<'a> {
pub children: NvArray<'a, NvList<'a>>,
pub parity: Option<u64>,
}
impl fmt::Debug for LabelVdevTreeRaidZ<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LabelVdevTreeRaidZ")
.field("count", &self.children.len())
.field("parity", &self.parity)
.finish()
}
}
impl LabelVdevTreeRaidZ<'_> {
const EXPECTED: [VdevTreeKey; 2] = [VdevTreeKey::Children, VdevTreeKey::NParity];
pub fn from_list<'a>(
list: &NvList<'a>,
) -> Result<LabelVdevTreeRaidZ<'a>, LabelVdevTreeDecodeError> {
let children_str = VdevTreeKey::Children.into();
let children = match list.get_nv_list_array(children_str)? {
Some(v) => v,
None => return Err(LabelVdevTreeDecodeError::Missing { name: children_str }),
};
let parity = list.get_u64(VdevTreeKey::NParity.into())?;
Ok(LabelVdevTreeRaidZ { children, parity })
}
}
#[derive(Debug)]
pub struct LabelVdevChild<'a> {
pub guid: u64,
pub id: u64,
pub path: &'a str,
pub vdev_type: VdevType,
pub create_txg: Option<u64>,
}
impl LabelVdevChild<'_> {
const EXPECTED: [VdevTreeKey; 5] = [
VdevTreeKey::CreateTxg,
VdevTreeKey::Guid,
VdevTreeKey::Id,
VdevTreeKey::Path,
VdevTreeKey::VdevType,
];
pub fn from_list<'a>(
list: &NvList<'a>,
) -> Result<LabelVdevChild<'a>, LabelVdevTreeDecodeError> {
let id_str = VdevTreeKey::Id.into();
let id = match list.get_u64(id_str)? {
Some(v) => v,
None => return Err(LabelVdevTreeDecodeError::Missing { name: id_str }),
};
let guid_str = VdevTreeKey::Guid.into();
let guid = match list.get_u64(guid_str)? {
Some(v) => v,
None => return Err(LabelVdevTreeDecodeError::Missing { name: guid_str }),
};
let path_str = VdevTreeKey::Path.into();
let path = match list.get_str(path_str)? {
Some(v) => v,
None => return Err(LabelVdevTreeDecodeError::Missing { name: path_str }),
};
let vdev_type_str = VdevTreeKey::VdevType.into();
let vdev_type = match list.get_str(vdev_type_str)? {
Some(v) => match VdevType::try_from(v) {
Ok(v) => v,
Err(_) => {
return Err(LabelVdevTreeDecodeError::UnknownVdevType {
vdev_type: v.into(),
})
}
},
None => {
return Err(LabelVdevTreeDecodeError::Missing {
name: vdev_type_str,
})
}
};
let create_txg = list.get_u64(VdevTreeKey::CreateTxg.into())?;
for pair_res in list {
let pair = pair_res?;
let pool_nv_name = match VdevTreeKey::try_from(pair.name) {
Ok(v) => v,
Err(_) => {
return Err(LabelVdevTreeDecodeError::Unknown {
name: pair.name.into(),
})
}
};
if !LabelVdevChild::EXPECTED.contains(&pool_nv_name) {
return Err(LabelVdevTreeDecodeError::Unexpected { name: pool_nv_name });
}
}
Ok(LabelVdevChild {
guid,
id,
path,
vdev_type,
create_txg,
})
}
}
#[derive(Debug)]
pub enum LabelVdevTreeDecodeError {
Missing {
name: &'static str,
},
Nv {
err: NvDecodeError,
},
Unexpected {
name: VdevTreeKey,
},
Unknown {
name: Fstr<16>,
},
UnknownVdevType {
vdev_type: Fstr<16>,
},
UnsupportedVdevType {
vdev_type: VdevType,
},
}
impl From<NvDecodeError> for LabelVdevTreeDecodeError {
fn from(err: NvDecodeError) -> Self {
LabelVdevTreeDecodeError::Nv { err }
}
}
impl fmt::Display for LabelVdevTreeDecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LabelVdevTreeDecodeError::Missing { name } => {
write!(f, "LabelVdevTree decode error, missing field '{name}'")
}
LabelVdevTreeDecodeError::Nv { err } => {
write!(f, "LabelVdevTree decode error | {err}")
}
LabelVdevTreeDecodeError::Unexpected { name } => {
write!(f, "LabelVdevTree decode error, unexpected field '{name}'")
}
LabelVdevTreeDecodeError::Unknown { name } => {
write!(f, "LabelVdevTree decode error, unknown field '{name}'")
}
LabelVdevTreeDecodeError::UnknownVdevType { vdev_type } => {
write!(
f,
"LabelVdevTree decode error, unknown vdev type '{vdev_type}'"
)
}
LabelVdevTreeDecodeError::UnsupportedVdevType { vdev_type } => {
write!(
f,
"LabelVdevTree decode error, unsupported vdev type '{vdev_type}'"
)
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for LabelVdevTreeDecodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
LabelVdevTreeDecodeError::Nv { err } => Some(err),
_ => None,
}
}
}