use core::fmt;
#[cfg(feature = "std")]
use std::error;
use crate::phys::{
BinaryDecodeError, BinaryDecoder, BinaryEncodeError, BinaryEncoder, BlockPointer,
BlockPointerDecodeError, BlockPointerEncodeError, ChecksumType, ChecksumTypeError,
CompressionType, CompressionTypeError, DmuType, DmuTypeError,
};
#[derive(Debug)]
pub struct Dnode {
pub bonus_len: usize,
pub bonus_type: DmuType,
pub checksum: ChecksumType,
pub compression: CompressionType,
pub data_block_size_sectors: u16,
pub extra_slots: u8,
pub dmu: DmuType,
pub indirect_block_shift: u8,
pub levels: u8,
pub max_block_id: u64,
pub tail: DnodeTail,
pub used: DnodeUsed,
pub user_obj_used_accounted: bool,
pub user_used_accounted: bool,
}
#[derive(Debug)]
pub enum DnodeUsed {
Bytes(u64),
Sectors(u64),
}
#[derive(Debug)]
pub enum DnodeTail {
Zero(DnodeTailZero),
One(DnodeTailOne),
Two(DnodeTailTwo),
Three(DnodeTailThree),
Spill(DnodeTailSpill),
}
#[derive(Debug)]
pub struct DnodeTailZero {
pub ptrs: [Option<BlockPointer>; 0],
pub bonus: [u8; DnodeTailZero::BONUS_SIZE],
}
impl DnodeTailZero {
pub const BONUS_SIZE: usize = 448;
}
#[derive(Debug)]
pub struct DnodeTailOne {
pub ptrs: [Option<BlockPointer>; 1],
pub bonus: [u8; DnodeTailOne::BONUS_SIZE],
}
impl DnodeTailOne {
pub const BONUS_SIZE: usize = DnodeTailZero::BONUS_SIZE - BlockPointer::SIZE;
}
#[derive(Debug)]
pub struct DnodeTailTwo {
pub ptrs: [Option<BlockPointer>; 2],
pub bonus: [u8; DnodeTailTwo::BONUS_SIZE],
}
impl DnodeTailTwo {
pub const BONUS_SIZE: usize = DnodeTailOne::BONUS_SIZE - BlockPointer::SIZE;
}
#[derive(Debug)]
pub struct DnodeTailThree {
pub ptrs: [Option<BlockPointer>; 3],
pub bonus: [u8; 64],
}
impl DnodeTailThree {
pub const BONUS_SIZE: usize = DnodeTailTwo::BONUS_SIZE - BlockPointer::SIZE;
}
#[derive(Debug)]
pub struct DnodeTailSpill {
pub ptrs: [Option<BlockPointer>; 1],
pub bonus: [u8; DnodeTailSpill::BONUS_SIZE],
pub spill: Option<BlockPointer>,
}
impl DnodeTailSpill {
pub const BONUS_SIZE: usize = DnodeTailOne::BONUS_SIZE - BlockPointer::SIZE;
}
impl Dnode {
pub const SIZE: usize = 512;
const PADDING_SIZE_A: usize = 3;
const PADDING_SIZE_B: usize = 32;
const FLAG_USED_BYTES: u8 = (1 << 0);
const FLAG_USER_USED_ACCOUNTED: u8 = (1 << 1);
const FLAG_BLOCK_POINTER: u8 = (1 << 2);
const FLAG_USER_OBJ_USED_ACCOUNTED: u8 = (1 << 3);
const FLAG_ALL: u8 = Dnode::FLAG_USED_BYTES
| Dnode::FLAG_USER_USED_ACCOUNTED
| Dnode::FLAG_BLOCK_POINTER
| Dnode::FLAG_USER_OBJ_USED_ACCOUNTED;
pub fn from_decoder(
decoder: &mut dyn BinaryDecoder<'_>,
) -> Result<Option<Dnode>, DnodeDecodeError> {
if decoder.is_skip_zeros(Dnode::SIZE)? {
return Ok(None);
}
let dmu = DmuType::try_from(decoder.get_u8()?)?;
let indirect_block_shift = decoder.get_u8()?;
let levels = decoder.get_u8()?;
let block_pointers_n = decoder.get_u8()?;
let bonus_type = DmuType::try_from(decoder.get_u8()?)?;
let checksum = ChecksumType::try_from(decoder.get_u8()?)?;
let compression = CompressionType::try_from(decoder.get_u8()?)?;
let flags = decoder.get_u8()?;
if (flags & Dnode::FLAG_ALL) != flags {
return Err(DnodeDecodeError::Flags { flags });
}
let is_spill = (flags & Dnode::FLAG_BLOCK_POINTER) != 0;
if is_spill && block_pointers_n != 1 {
return Err(DnodeDecodeError::SpillBlockPointerCount {
count: block_pointers_n,
});
}
let data_block_size_sectors = decoder.get_u16()?;
let bonus_len = usize::from(decoder.get_u16()?);
let extra_slots = decoder.get_u8()?;
decoder.skip_zeros(Dnode::PADDING_SIZE_A)?;
let max_block_id = decoder.get_u64()?;
let used = decoder.get_u64()?;
let used = if (flags & Dnode::FLAG_USED_BYTES) != 0 {
DnodeUsed::Bytes(used)
} else {
DnodeUsed::Sectors(used)
};
decoder.skip_zeros(Dnode::PADDING_SIZE_B)?;
let max_bonus_len: usize;
let tail = match block_pointers_n {
0 => {
let tail = DnodeTailZero {
ptrs: [],
bonus: decoder
.get_bytes_n(DnodeTailZero::BONUS_SIZE)?
.try_into()
.unwrap(),
};
max_bonus_len = tail.bonus.len();
DnodeTail::Zero(tail)
}
1 => {
if is_spill {
let tail = DnodeTailSpill {
ptrs: [BlockPointer::from_decoder(decoder)?],
bonus: decoder
.get_bytes_n(DnodeTailSpill::BONUS_SIZE)?
.try_into()
.unwrap(),
spill: BlockPointer::from_decoder(decoder)?,
};
max_bonus_len = tail.bonus.len();
DnodeTail::Spill(tail)
} else {
let tail = DnodeTailOne {
ptrs: [BlockPointer::from_decoder(decoder)?],
bonus: decoder
.get_bytes_n(DnodeTailOne::BONUS_SIZE)?
.try_into()
.unwrap(),
};
max_bonus_len = tail.bonus.len();
DnodeTail::One(tail)
}
}
2 => {
let tail = DnodeTailTwo {
ptrs: [
BlockPointer::from_decoder(decoder)?,
BlockPointer::from_decoder(decoder)?,
],
bonus: decoder
.get_bytes_n(DnodeTailTwo::BONUS_SIZE)?
.try_into()
.unwrap(),
};
max_bonus_len = tail.bonus.len();
DnodeTail::Two(tail)
}
3 => {
let tail = DnodeTailThree {
ptrs: [
BlockPointer::from_decoder(decoder)?,
BlockPointer::from_decoder(decoder)?,
BlockPointer::from_decoder(decoder)?,
],
bonus: decoder
.get_bytes_n(DnodeTailThree::BONUS_SIZE)?
.try_into()
.unwrap(),
};
max_bonus_len = tail.bonus.len();
DnodeTail::Three(tail)
}
count => return Err(DnodeDecodeError::BlockPointerCount { count }),
};
if bonus_len > max_bonus_len {
return Err(DnodeDecodeError::BonusLength { length: bonus_len });
}
Ok(Some(Dnode {
bonus_len,
bonus_type,
checksum,
compression,
data_block_size_sectors,
dmu,
extra_slots,
indirect_block_shift,
levels,
max_block_id,
tail,
used,
user_obj_used_accounted: (flags & Dnode::FLAG_USER_OBJ_USED_ACCOUNTED) != 0,
user_used_accounted: (flags & Dnode::FLAG_USER_USED_ACCOUNTED) != 0,
}))
}
pub fn to_encoder(&self, encoder: &mut dyn BinaryEncoder<'_>) -> Result<(), DnodeEncodeError> {
encoder.put_u8(self.dmu.into())?;
encoder.put_u8(self.indirect_block_shift)?;
encoder.put_u8(self.levels)?;
encoder.put_u8(self.pointers().len() as u8)?;
encoder.put_u8(self.bonus_type.into())?;
encoder.put_u8(self.checksum.into())?;
encoder.put_u8(self.compression.into())?;
let flags = match self.used {
DnodeUsed::Bytes(_) => Dnode::FLAG_USED_BYTES,
_ => 0,
} | if self.user_used_accounted {
Dnode::FLAG_USER_USED_ACCOUNTED
} else {
0
} | if self.user_obj_used_accounted {
Dnode::FLAG_USER_OBJ_USED_ACCOUNTED
} else {
0
} | match &self.tail {
DnodeTail::Spill(_) => Dnode::FLAG_BLOCK_POINTER,
_ => 0,
};
encoder.put_u8(flags)?;
encoder.put_u16(self.data_block_size_sectors)?;
if self.bonus_len > self.bonus_capacity().len() {
return Err(DnodeEncodeError::BonusLength {
length: self.bonus_len,
});
}
encoder.put_u16(self.bonus_len as u16)?;
encoder.put_u8(self.extra_slots)?;
encoder.put_zeros(Dnode::PADDING_SIZE_A)?;
encoder.put_u64(self.max_block_id)?;
encoder.put_u64(match self.used {
DnodeUsed::Bytes(v) => v,
DnodeUsed::Sectors(v) => v,
})?;
encoder.put_zeros(Dnode::PADDING_SIZE_B)?;
for ptr in self.pointers() {
match ptr {
Some(ptr) => ptr.to_encoder(encoder)?,
None => BlockPointer::empty_to_encoder(encoder)?,
}
}
encoder.put_bytes(self.bonus_capacity())?;
if let DnodeTail::Spill(tail) = &self.tail {
match &tail.spill {
Some(ptr) => ptr.to_encoder(encoder)?,
None => BlockPointer::empty_to_encoder(encoder)?,
}
}
Ok(())
}
pub fn empty_to_encoder(encoder: &mut dyn BinaryEncoder<'_>) -> Result<(), DnodeEncodeError> {
Ok(encoder.put_zeros(Dnode::SIZE)?)
}
pub fn option_to_encoder(
dnode: &Option<Dnode>,
encoder: &mut dyn BinaryEncoder<'_>,
) -> Result<(), DnodeEncodeError> {
match dnode {
Some(v) => v.to_encoder(encoder),
None => Ok(Dnode::empty_to_encoder(encoder)?),
}
}
pub fn bonus_capacity(&self) -> &[u8] {
match &self.tail {
DnodeTail::Zero(tail) => &tail.bonus,
DnodeTail::One(tail) => &tail.bonus,
DnodeTail::Two(tail) => &tail.bonus,
DnodeTail::Three(tail) => &tail.bonus,
DnodeTail::Spill(tail) => &tail.bonus,
}
}
pub fn bonus_used(&self) -> &[u8] {
match &self.tail {
DnodeTail::Zero(tail) => &tail.bonus[0..(self.bonus_len)],
DnodeTail::One(tail) => &tail.bonus[0..(self.bonus_len)],
DnodeTail::Two(tail) => &tail.bonus[0..(self.bonus_len)],
DnodeTail::Three(tail) => &tail.bonus[0..(self.bonus_len)],
DnodeTail::Spill(tail) => &tail.bonus[0..(self.bonus_len)],
}
}
pub fn pointers(&self) -> &[Option<BlockPointer>] {
match &self.tail {
DnodeTail::Zero(tail) => &tail.ptrs,
DnodeTail::One(tail) => &tail.ptrs,
DnodeTail::Two(tail) => &tail.ptrs,
DnodeTail::Three(tail) => &tail.ptrs,
DnodeTail::Spill(tail) => &tail.ptrs,
}
}
}
#[derive(Debug)]
pub enum DnodeDecodeError {
Binary {
err: BinaryDecodeError,
},
BlockPointer {
err: BlockPointerDecodeError,
},
BlockPointerCount {
count: u8,
},
BonusLength {
length: usize,
},
ChecksumType {
err: ChecksumTypeError,
},
CompressionType {
err: CompressionTypeError,
},
DmuType {
err: DmuTypeError,
},
Flags {
flags: u8,
},
SpillBlockPointerCount {
count: u8,
},
}
impl From<BinaryDecodeError> for DnodeDecodeError {
fn from(err: BinaryDecodeError) -> Self {
DnodeDecodeError::Binary { err }
}
}
impl From<BlockPointerDecodeError> for DnodeDecodeError {
fn from(err: BlockPointerDecodeError) -> Self {
DnodeDecodeError::BlockPointer { err }
}
}
impl From<ChecksumTypeError> for DnodeDecodeError {
fn from(err: ChecksumTypeError) -> Self {
DnodeDecodeError::ChecksumType { err }
}
}
impl From<CompressionTypeError> for DnodeDecodeError {
fn from(err: CompressionTypeError) -> Self {
DnodeDecodeError::CompressionType { err }
}
}
impl From<DmuTypeError> for DnodeDecodeError {
fn from(err: DmuTypeError) -> Self {
DnodeDecodeError::DmuType { err }
}
}
impl fmt::Display for DnodeDecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DnodeDecodeError::Binary { err } => {
write!(f, "Dnode decode error | {err}")
}
DnodeDecodeError::BlockPointer { err } => {
write!(f, "Dnode decode error | {err}")
}
DnodeDecodeError::BlockPointerCount { count } => {
write!(f, "Dnode decode error, block pointer count {count}")
}
DnodeDecodeError::BonusLength { length } => {
write!(f, "Dnode decode error, invalid bonus length {length}")
}
DnodeDecodeError::ChecksumType { err } => {
write!(f, "Dnode decode error | {err}")
}
DnodeDecodeError::CompressionType { err } => {
write!(f, "Dnode decode error | {err}")
}
DnodeDecodeError::DmuType { err } => {
write!(f, "Dnode decode error | {err}")
}
DnodeDecodeError::Flags { flags } => {
write!(f, "Dnode decode error, unknown flags {flags:#02x}")
}
DnodeDecodeError::SpillBlockPointerCount { count } => {
write!(
f,
"Dnode decode error, invalid spill block pointer count {count}"
)
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for DnodeDecodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
DnodeDecodeError::Binary { err } => Some(err),
DnodeDecodeError::BlockPointer { err } => Some(err),
DnodeDecodeError::ChecksumType { err } => Some(err),
DnodeDecodeError::CompressionType { err } => Some(err),
DnodeDecodeError::DmuType { err } => Some(err),
_ => None,
}
}
}
#[derive(Debug)]
pub enum DnodeEncodeError {
Binary {
err: BinaryEncodeError,
},
BlockPointer {
err: BlockPointerEncodeError,
},
BonusLength {
length: usize,
},
}
impl From<BinaryEncodeError> for DnodeEncodeError {
fn from(err: BinaryEncodeError) -> Self {
DnodeEncodeError::Binary { err }
}
}
impl From<BlockPointerEncodeError> for DnodeEncodeError {
fn from(err: BlockPointerEncodeError) -> Self {
DnodeEncodeError::BlockPointer { err }
}
}
impl fmt::Display for DnodeEncodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DnodeEncodeError::Binary { err } => {
write!(f, "Dnode encode error | {err}")
}
DnodeEncodeError::BlockPointer { err } => {
write!(f, "Dnode encode error | {err}")
}
DnodeEncodeError::BonusLength { length } => {
write!(f, "Dnode encode error, invalid bonus length {length}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for DnodeEncodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
DnodeEncodeError::Binary { err } => Some(err),
DnodeEncodeError::BlockPointer { err } => Some(err),
_ => None,
}
}
}