use core::fmt;
use core::fmt::Display;
#[cfg(feature = "std")]
use std::error;
use crate::phys::{
BinaryDecodeError, BinaryDecoder, BinaryEncodeError, BinaryEncoder, Dnode, DnodeDecodeError,
DnodeEncodeError, ZilHeader, ZilHeaderDecodeError, ZilHeaderEncodeError,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u64)]
pub enum ObjectSetType {
None = 0,
Meta = 1,
ZFS = 2,
ZVol = 3,
Other = 4,
Any = 5,
}
impl Display for ObjectSetType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ObjectSetType::None => write!(f, "None"),
ObjectSetType::Meta => write!(f, "Meta"),
ObjectSetType::ZFS => write!(f, "ZFS"),
ObjectSetType::ZVol => write!(f, "ZVol"),
ObjectSetType::Other => write!(f, "Other"),
ObjectSetType::Any => write!(f, "Any"),
}
}
}
impl From<ObjectSetType> for u64 {
fn from(val: ObjectSetType) -> u64 {
val as u64
}
}
impl TryFrom<u64> for ObjectSetType {
type Error = ObjectSetTypeError;
fn try_from(os_type: u64) -> Result<Self, Self::Error> {
match os_type {
0 => Ok(ObjectSetType::None),
1 => Ok(ObjectSetType::Meta),
2 => Ok(ObjectSetType::ZFS),
3 => Ok(ObjectSetType::ZVol),
4 => Ok(ObjectSetType::Other),
5 => Ok(ObjectSetType::Any),
_ => Err(ObjectSetTypeError::Unknown { os_type }),
}
}
}
#[derive(Debug)]
pub enum ObjectSetTypeError {
Unknown {
os_type: u64,
},
}
impl fmt::Display for ObjectSetTypeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ObjectSetTypeError::Unknown { os_type } => {
write!(f, "Unknown ObjectSetType {os_type}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for ObjectSetTypeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
#[derive(Debug)]
pub struct ObjectSet {
pub dnode: Dnode,
pub zil_header: ZilHeader,
pub os_type: ObjectSetType,
pub user_accounting_complete: bool,
pub user_object_accounting_complete: bool,
pub project_quota_complete: bool,
pub portable_mac: [u8; ObjectSet::MAC_LEN],
pub local_mac: [u8; ObjectSet::MAC_LEN],
pub extension: ObjectSetExtension,
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum ObjectSetExtension {
Zero {},
Two {
user_used: Option<Dnode>,
group_used: Option<Dnode>,
},
Three {
user_used: Option<Dnode>,
group_used: Option<Dnode>,
project_used: Option<Dnode>,
},
}
impl ObjectSet {
pub const SIZE_EXT_0: usize = (Dnode::SIZE
+ ZilHeader::SIZE
+ 16
+ ObjectSet::MAC_LEN * 2
+ ObjectSet::PADDING_SIZE_NONE);
pub const SIZE_EXT_2: usize = (ObjectSet::SIZE_EXT_0 + 2 * Dnode::SIZE);
pub const SIZE_EXT_3: usize =
(ObjectSet::SIZE_EXT_2 + Dnode::SIZE + ObjectSet::PADDING_SIZE_THREE);
pub const MAC_LEN: usize = 32;
const PADDING_SIZE_NONE: usize = 240;
const PADDING_SIZE_THREE: usize = 1536;
const FLAG_USER_ACCOUNTING_COMPLETE: u64 = 1 << 0;
const FLAG_USER_OBJECT_ACCOUNTING_COMPLETE: u64 = 1 << 1;
const FLAG_PROJECT_QUOTA_COMPLETE: u64 = 1 << 2;
const FLAG_ALL: u64 = ObjectSet::FLAG_USER_ACCOUNTING_COMPLETE
| ObjectSet::FLAG_USER_OBJECT_ACCOUNTING_COMPLETE
| ObjectSet::FLAG_PROJECT_QUOTA_COMPLETE;
pub fn from_decoder(
decoder: &mut dyn BinaryDecoder<'_>,
) -> Result<ObjectSet, ObjectSetDecodeError> {
let dnode = match Dnode::from_decoder(decoder)? {
Some(dnode) => dnode,
None => return Err(ObjectSetDecodeError::EmptyDnode {}),
};
let zil_header = ZilHeader::from_decoder(decoder)?;
let os_type = decoder.get_u64()?;
let os_type = ObjectSetType::try_from(os_type)?;
let flags = decoder.get_u64()?;
if (flags & ObjectSet::FLAG_ALL) != flags {
return Err(ObjectSetDecodeError::Flags { flags });
}
let portable_mac = decoder.get_bytes_n(ObjectSet::MAC_LEN)?.try_into().unwrap();
let local_mac = decoder.get_bytes_n(ObjectSet::MAC_LEN)?.try_into().unwrap();
decoder.skip_zeros(ObjectSet::PADDING_SIZE_NONE)?;
let mut extension = ObjectSetExtension::Zero {};
if !decoder.is_empty() {
let user_used = Dnode::from_decoder(decoder)?;
let group_used = Dnode::from_decoder(decoder)?;
if decoder.is_empty() {
extension = ObjectSetExtension::Two {
user_used,
group_used,
};
} else {
let project_used = Dnode::from_decoder(decoder)?;
extension = ObjectSetExtension::Three {
user_used,
group_used,
project_used,
};
decoder.skip_zeros(ObjectSet::PADDING_SIZE_THREE)?;
}
}
Ok(ObjectSet {
dnode,
zil_header,
os_type,
user_accounting_complete: (flags & ObjectSet::FLAG_USER_ACCOUNTING_COMPLETE) != 0,
user_object_accounting_complete: (flags
& ObjectSet::FLAG_USER_OBJECT_ACCOUNTING_COMPLETE)
!= 0,
project_quota_complete: (flags & ObjectSet::FLAG_PROJECT_QUOTA_COMPLETE) != 0,
portable_mac,
local_mac,
extension,
})
}
pub fn to_encoder(
&self,
encoder: &mut dyn BinaryEncoder<'_>,
) -> Result<(), ObjectSetEncodeError> {
self.dnode.to_encoder(encoder)?;
self.zil_header.to_encoder(encoder)?;
encoder.put_u64(u64::from(self.os_type))?;
let flags = if self.user_accounting_complete {
ObjectSet::FLAG_USER_ACCOUNTING_COMPLETE
} else {
0
} | if self.user_object_accounting_complete {
ObjectSet::FLAG_USER_OBJECT_ACCOUNTING_COMPLETE
} else {
0
} | if self.project_quota_complete {
ObjectSet::FLAG_PROJECT_QUOTA_COMPLETE
} else {
0
};
encoder.put_u64(flags)?;
encoder.put_bytes(&self.portable_mac)?;
encoder.put_bytes(&self.local_mac)?;
encoder.put_zeros(ObjectSet::PADDING_SIZE_NONE)?;
match &self.extension {
ObjectSetExtension::Zero {} => (),
ObjectSetExtension::Two {
user_used,
group_used,
} => {
Dnode::option_to_encoder(user_used, encoder)?;
Dnode::option_to_encoder(group_used, encoder)?;
}
ObjectSetExtension::Three {
user_used,
group_used,
project_used,
} => {
Dnode::option_to_encoder(user_used, encoder)?;
Dnode::option_to_encoder(group_used, encoder)?;
Dnode::option_to_encoder(project_used, encoder)?;
encoder.put_zeros(ObjectSet::PADDING_SIZE_THREE)?;
}
};
Ok(())
}
}
#[derive(Debug)]
pub enum ObjectSetDecodeError {
Binary {
err: BinaryDecodeError,
},
Dnode {
err: DnodeDecodeError,
},
EmptyDnode {},
Flags {
flags: u64,
},
ObjectSetType {
err: ObjectSetTypeError,
},
ZilHeader {
err: ZilHeaderDecodeError,
},
}
impl From<BinaryDecodeError> for ObjectSetDecodeError {
fn from(err: BinaryDecodeError) -> Self {
ObjectSetDecodeError::Binary { err }
}
}
impl From<DnodeDecodeError> for ObjectSetDecodeError {
fn from(err: DnodeDecodeError) -> Self {
ObjectSetDecodeError::Dnode { err }
}
}
impl From<ObjectSetTypeError> for ObjectSetDecodeError {
fn from(err: ObjectSetTypeError) -> Self {
ObjectSetDecodeError::ObjectSetType { err }
}
}
impl From<ZilHeaderDecodeError> for ObjectSetDecodeError {
fn from(err: ZilHeaderDecodeError) -> Self {
ObjectSetDecodeError::ZilHeader { err }
}
}
impl fmt::Display for ObjectSetDecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ObjectSetDecodeError::Binary { err } => {
write!(f, "ObjectSet decode error | {err}")
}
ObjectSetDecodeError::Dnode { err } => {
write!(f, "ObjectSet decode error | {err}")
}
ObjectSetDecodeError::EmptyDnode {} => {
write!(f, "ObjectSet decode error, empty dnode")
}
ObjectSetDecodeError::Flags { flags } => {
write!(f, "ObjectSet decode error, unknown flags {flags:#016x}")
}
ObjectSetDecodeError::ObjectSetType { err } => {
write!(f, "ObjectSet decode error | {err}")
}
ObjectSetDecodeError::ZilHeader { err } => {
write!(f, "ObjectSet decode error | {err}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for ObjectSetDecodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
ObjectSetDecodeError::Binary { err } => Some(err),
ObjectSetDecodeError::Dnode { err } => Some(err),
ObjectSetDecodeError::ObjectSetType { err } => Some(err),
ObjectSetDecodeError::ZilHeader { err } => Some(err),
_ => None,
}
}
}
#[derive(Debug)]
pub enum ObjectSetEncodeError {
Binary {
err: BinaryEncodeError,
},
Dnode {
err: DnodeEncodeError,
},
ZilHeader {
err: ZilHeaderEncodeError,
},
}
impl From<BinaryEncodeError> for ObjectSetEncodeError {
fn from(err: BinaryEncodeError) -> Self {
ObjectSetEncodeError::Binary { err }
}
}
impl From<DnodeEncodeError> for ObjectSetEncodeError {
fn from(err: DnodeEncodeError) -> Self {
ObjectSetEncodeError::Dnode { err }
}
}
impl From<ZilHeaderEncodeError> for ObjectSetEncodeError {
fn from(err: ZilHeaderEncodeError) -> Self {
ObjectSetEncodeError::ZilHeader { err }
}
}
impl fmt::Display for ObjectSetEncodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ObjectSetEncodeError::Binary { err } => {
write!(f, "ObjectSet encode error | {err}")
}
ObjectSetEncodeError::Dnode { err } => {
write!(f, "ObjectSet encode error | {err}")
}
ObjectSetEncodeError::ZilHeader { err } => {
write!(f, "ObjectSet encode error | {err}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for ObjectSetEncodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
ObjectSetEncodeError::Binary { err } => Some(err),
ObjectSetEncodeError::Dnode { err } => Some(err),
ObjectSetEncodeError::ZilHeader { err } => Some(err),
}
}
}