use core::fmt;
use std::ffi::OsStr;
use std::path::{Component, Path};
use deku::prelude::*;
use crate::BackhandError;
use crate::v3::unix_string::OsStrExt;
#[derive(Debug, DekuRead, Clone, PartialEq, Eq)]
#[deku(ctx = "type_endian: deku::ctx::Endian, order: deku::ctx::Order")]
#[deku(bit_order = "order")]
#[deku(endian = "type_endian")]
pub struct Dir {
#[deku(assert = "*count <= 256", bytes = "1")]
pub(crate) count: u32,
pub(crate) start: u32,
pub(crate) inode_num: i32,
#[deku(count = "*count + 1")]
pub(crate) dir_entries: Vec<DirEntry>,
}
impl Dir {
pub fn new(lowest_inode: u32) -> Self {
Self {
count: u32::default(),
start: u32::default(),
inode_num: lowest_inode as i32,
dir_entries: vec![],
}
}
pub fn push(&mut self, entry: DirEntry) {
self.dir_entries.push(entry);
self.count = (self.dir_entries.len() - 1) as u32;
}
}
#[derive(Debug, DekuRead, Clone, Copy, PartialEq, Eq)]
#[deku(id_type = "u8", bits = "3")]
#[deku(endian = "endian", bit_order = "order", ctx = "endian: deku::ctx::Endian, order: deku::ctx::Order")]
#[rustfmt::skip]
#[repr(u8)]
pub enum DirInodeId {
BasicDirectory = 1,
BasicFile = 2,
BasicSymlink = 3,
BasicBlockDevice = 4,
BasicCharacterDevice = 5,
BasicNamedPipe = 6,
BasicSocket = 7,
ExtendedDirectory = 8,
ExtendedFile = 9,
ExtendedSymlink = 10,
ExtendedBlockDevice = 11,
ExtendedCharacterDevice = 12,
ExtendedNamedPipe = 13,
ExtendedSocket = 14
}
#[derive(DekuRead, Clone, PartialEq, Eq)]
#[deku(
endian = "endian",
bit_order = "order",
ctx = "endian: deku::ctx::Endian, order: deku::ctx::Order"
)]
pub struct DirEntry {
#[deku(bits = "13")]
pub(crate) offset: u16,
pub(crate) t: DirInodeId,
#[deku(bytes = "1")]
pub(crate) name_size: u16,
pub(crate) inode_offset: i16,
#[deku(count = "*name_size + 1")]
pub(crate) name: Vec<u8>,
}
impl fmt::Debug for DirEntry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DirEntry")
.field("offset", &self.offset)
.field("t", &self.t)
.field("name_size", &self.name_size)
.field("inode_offset", &self.inode_offset)
.field("name", &self.name())
.finish()
}
}
impl DirEntry {
pub fn name(&self) -> Result<&Path, BackhandError> {
if self.name == Component::RootDir.as_os_str().as_bytes() {
return Ok(Path::new(Component::RootDir.as_os_str()));
}
let path = Path::new(OsStr::from_bytes(&self.name));
let filename = path.file_name().map(OsStrExt::as_bytes);
if filename != Some(&self.name) {
return Err(BackhandError::InvalidFilePath);
}
Ok(path)
}
}
#[derive(DekuRead, Clone, PartialEq, Eq)]
#[deku(
ctx = "endian: deku::ctx::Endian, order: deku::ctx::Order",
endian = "endian",
bit_order = "order"
)]
pub struct DirectoryIndex {
pub(crate) index: u32,
pub(crate) start: u32,
#[deku(assert = "*name_size < 100")]
pub(crate) name_size: u8,
#[deku(count = "*name_size + 1")]
pub(crate) name: Vec<u8>,
}
impl fmt::Debug for DirectoryIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DirectoryIndex")
.field("index", &self.index)
.field("start", &self.start)
.field("name_size", &self.name_size)
.field("name", &self.name())
.finish()
}
}
impl DirectoryIndex {
pub fn name(&self) -> String {
core::str::from_utf8(&self.name).unwrap().to_string()
}
}