use core::fmt;
use core::fmt::Display;
#[cfg(feature = "std")]
use std::error;
use crate::phys::{
Acl, AclDecodeError, AclEncodeError, BinaryDecodeError, BinaryDecoder, BinaryEncodeError,
BinaryEncoder,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ZplVersion {
V1 = 1,
V2 = 2,
V3 = 3,
V4 = 4,
V5 = 5,
}
impl From<ZplVersion> for u64 {
fn from(val: ZplVersion) -> u64 {
val as u64
}
}
impl Display for ZplVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", u64::from(*self))
}
}
impl TryFrom<u64> for ZplVersion {
type Error = ZplVersionError;
fn try_from(version: u64) -> Result<Self, Self::Error> {
match version {
1 => Ok(ZplVersion::V1),
2 => Ok(ZplVersion::V2),
3 => Ok(ZplVersion::V3),
4 => Ok(ZplVersion::V4),
5 => Ok(ZplVersion::V5),
_ => Err(ZplVersionError::Unknown { version }),
}
}
}
#[derive(Debug)]
pub enum ZplVersionError {
Unknown {
version: u64,
},
}
impl fmt::Display for ZplVersionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ZplVersionError::Unknown { version } => {
write!(f, "Unknown ZplVersion {version}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for ZplVersionError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum ZnodeFileType {
Fifo = 1,
Character = 2,
Directory = 4,
Block = 6,
Regular = 8,
Symlink = 10,
Socket = 12,
Door = 13,
EventPort = 14,
}
impl Display for ZnodeFileType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ZnodeFileType::Fifo => write!(f, "Fifo"),
ZnodeFileType::Character => write!(f, "Character"),
ZnodeFileType::Directory => write!(f, "Directory"),
ZnodeFileType::Block => write!(f, "Block"),
ZnodeFileType::Regular => write!(f, "Regular"),
ZnodeFileType::Symlink => write!(f, "Symlink"),
ZnodeFileType::Socket => write!(f, "Socket"),
ZnodeFileType::Door => write!(f, "Door"),
ZnodeFileType::EventPort => write!(f, "EventPort"),
}
}
}
impl From<ZnodeFileType> for u8 {
fn from(val: ZnodeFileType) -> u8 {
val as u8
}
}
impl TryFrom<u8> for ZnodeFileType {
type Error = ZnodeFileTypeError;
fn try_from(file_type: u8) -> Result<Self, Self::Error> {
match file_type {
1 => Ok(ZnodeFileType::Fifo),
2 => Ok(ZnodeFileType::Character),
4 => Ok(ZnodeFileType::Directory),
6 => Ok(ZnodeFileType::Block),
8 => Ok(ZnodeFileType::Regular),
10 => Ok(ZnodeFileType::Symlink),
12 => Ok(ZnodeFileType::Socket),
13 => Ok(ZnodeFileType::Door),
14 => Ok(ZnodeFileType::EventPort),
_ => Err(ZnodeFileTypeError::Unknown { file_type }),
}
}
}
#[derive(Debug)]
pub enum ZnodeFileTypeError {
Unknown {
file_type: u8,
},
}
impl fmt::Display for ZnodeFileTypeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ZnodeFileTypeError::Unknown { file_type } => {
write!(f, "Unknown ZnodeFileType {file_type}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for ZnodeFileTypeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
#[derive(Debug)]
pub struct ZnodeTime {
pub seconds: u64,
pub nanoseconds: u64,
}
impl ZnodeTime {
pub fn from_decoder(
decoder: &mut dyn BinaryDecoder<'_>,
) -> Result<ZnodeTime, BinaryDecodeError> {
Ok(ZnodeTime {
seconds: decoder.get_u64()?,
nanoseconds: decoder.get_u64()?,
})
}
pub fn to_encoder(&self, encoder: &mut dyn BinaryEncoder<'_>) -> Result<(), BinaryEncodeError> {
encoder.put_u64(self.seconds)?;
encoder.put_u64(self.nanoseconds)?;
Ok(())
}
}
pub struct ZnodePermission;
impl ZnodePermission {
pub const SUID: u16 = 0o4000;
pub const SGID: u16 = 0o2000;
pub const STCK: u16 = 0o1000;
pub const USR_R: u16 = 0o0400;
pub const USR_W: u16 = 0o0200;
pub const USR_X: u16 = 0o0100;
pub const GRP_R: u16 = 0o0040;
pub const GRP_W: u16 = 0o0020;
pub const GRP_X: u16 = 0o0010;
pub const OTH_R: u16 = 0o0004;
pub const OTH_W: u16 = 0o0002;
pub const OTH_X: u16 = 0o0001;
pub const MASK: u16 = 0o7777;
}
#[derive(Debug)]
pub struct Znode {
pub access_time: ZnodeTime,
pub modified_time: ZnodeTime,
pub change_time: ZnodeTime,
pub creation_time: ZnodeTime,
pub creation_txg: u64,
pub permission_bits: u16,
pub file_type: ZnodeFileType,
pub size: u64,
pub parent_object_id: u64,
pub num_links: u64,
pub xattr_object_id: Option<u64>,
pub device_number: u64,
pub flags: u64,
pub user_id: u64,
pub group_id: u64,
pub extra_attributes: u64,
pub acl: Acl,
}
impl Znode {
pub const SIZE: usize = 512;
const PADDING_SIZE: usize = 24;
const MODE_UNKNOWN_MASK: u64 = u64::MAX ^ ((1 << 16) - 1);
const MODE_FILE_TYPE_MASK_DOWN_SHIFTED: u64 = 0xf;
const MODE_FILE_TYPE_SHIFT: usize = 12;
pub fn from_decoder(decoder: &mut dyn BinaryDecoder<'_>) -> Result<Znode, ZnodeDecodeError> {
let access_time = ZnodeTime::from_decoder(decoder)?;
let modified_time = ZnodeTime::from_decoder(decoder)?;
let change_time = ZnodeTime::from_decoder(decoder)?;
let creation_time = ZnodeTime::from_decoder(decoder)?;
let creation_txg = decoder.get_u64()?;
let mode = decoder.get_u64()?;
if (mode & Znode::MODE_UNKNOWN_MASK) != 0 {
return Err(ZnodeDecodeError::Mode { mode });
}
let permission_bits = (mode & u64::from(ZnodePermission::MASK)) as u16;
let file_type =
(mode >> Znode::MODE_FILE_TYPE_SHIFT) & Znode::MODE_FILE_TYPE_MASK_DOWN_SHIFTED;
let file_type = ZnodeFileType::try_from(file_type as u8)?;
let size = decoder.get_u64()?;
let parent_object_id = decoder.get_u64()?;
if parent_object_id == 0 {
return Err(ZnodeDecodeError::MissingParentObjectId {});
}
let num_links = decoder.get_u64()?;
let xattr_object_id = match decoder.get_u64()? {
0 => None,
v => Some(v),
};
let device_number = decoder.get_u64()?;
let flags = decoder.get_u64()?;
let user_id = decoder.get_u64()?;
let group_id = decoder.get_u64()?;
let extra_attributes = decoder.get_u64()?;
decoder.skip_zeros(Znode::PADDING_SIZE)?;
Ok(Znode {
access_time,
modified_time,
change_time,
creation_time,
creation_txg,
permission_bits,
file_type,
size,
parent_object_id,
num_links,
xattr_object_id,
device_number,
flags,
user_id,
group_id,
extra_attributes,
acl: Acl::from_decoder(decoder)?,
})
}
pub fn to_encoder(&self, encoder: &mut dyn BinaryEncoder<'_>) -> Result<(), ZnodeEncodeError> {
self.access_time.to_encoder(encoder)?;
self.modified_time.to_encoder(encoder)?;
self.change_time.to_encoder(encoder)?;
self.creation_time.to_encoder(encoder)?;
encoder.put_u64(self.creation_txg)?;
if (self.permission_bits & ZnodePermission::MASK) != self.permission_bits {
return Err(ZnodeEncodeError::Permissions {
permissions: self.permission_bits,
});
}
let mode = u64::from(u8::from(self.file_type)) << Znode::MODE_FILE_TYPE_SHIFT;
let mode = mode | u64::from(self.permission_bits);
encoder.put_u64(mode)?;
encoder.put_u64(self.size)?;
if self.parent_object_id == 0 {
return Err(ZnodeEncodeError::MissingParentObjectId {});
}
encoder.put_u64(self.parent_object_id)?;
encoder.put_u64(self.num_links)?;
encoder.put_u64(self.xattr_object_id.unwrap_or(0))?;
encoder.put_u64(self.device_number)?;
encoder.put_u64(self.flags)?;
encoder.put_u64(self.user_id)?;
encoder.put_u64(self.group_id)?;
encoder.put_u64(self.extra_attributes)?;
encoder.put_zeros(Znode::PADDING_SIZE)?;
self.acl.to_encoder(encoder)?;
Ok(())
}
}
#[derive(Debug)]
pub enum ZnodeDecodeError {
Acl {
err: AclDecodeError,
},
Binary {
err: BinaryDecodeError,
},
FileType {
err: ZnodeFileTypeError,
},
MissingParentObjectId {},
Mode {
mode: u64,
},
}
impl From<AclDecodeError> for ZnodeDecodeError {
fn from(err: AclDecodeError) -> Self {
ZnodeDecodeError::Acl { err }
}
}
impl From<BinaryDecodeError> for ZnodeDecodeError {
fn from(err: BinaryDecodeError) -> Self {
ZnodeDecodeError::Binary { err }
}
}
impl From<ZnodeFileTypeError> for ZnodeDecodeError {
fn from(err: ZnodeFileTypeError) -> Self {
ZnodeDecodeError::FileType { err }
}
}
impl fmt::Display for ZnodeDecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ZnodeDecodeError::Acl { err } => {
write!(f, "Znode decode error | {err}")
}
ZnodeDecodeError::Binary { err } => {
write!(f, "Znode decode error | {err}")
}
ZnodeDecodeError::FileType { err } => {
write!(f, "Znode decode error | {err}")
}
ZnodeDecodeError::MissingParentObjectId {} => {
write!(f, "Znode decode error, missing parent object id")
}
ZnodeDecodeError::Mode { mode } => {
write!(f, "Znode decode error, unknown mode {mode}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for ZnodeDecodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
ZnodeDecodeError::Acl { err } => Some(err),
ZnodeDecodeError::Binary { err } => Some(err),
ZnodeDecodeError::FileType { err } => Some(err),
_ => None,
}
}
}
#[derive(Debug)]
pub enum ZnodeEncodeError {
Acl {
err: AclEncodeError,
},
Binary {
err: BinaryEncodeError,
},
MissingParentObjectId {},
Permissions {
permissions: u16,
},
}
impl From<AclEncodeError> for ZnodeEncodeError {
fn from(err: AclEncodeError) -> Self {
ZnodeEncodeError::Acl { err }
}
}
impl From<BinaryEncodeError> for ZnodeEncodeError {
fn from(err: BinaryEncodeError) -> Self {
ZnodeEncodeError::Binary { err }
}
}
impl fmt::Display for ZnodeEncodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ZnodeEncodeError::Acl { err } => {
write!(f, "Znode encode error | {err}")
}
ZnodeEncodeError::Binary { err } => {
write!(f, "Znode encode error | {err}")
}
ZnodeEncodeError::MissingParentObjectId {} => {
write!(f, "Znode encode error, missing parent object id")
}
ZnodeEncodeError::Permissions { permissions } => {
write!(f, "Znode encode error, unknown permission {permissions}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for ZnodeEncodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
ZnodeEncodeError::Acl { err } => Some(err),
ZnodeEncodeError::Binary { err } => Some(err),
_ => None,
}
}
}