Files
CappuccinOS/src/drivers/fs/initramfs/mod.rs

980 lines
30 KiB
Rust
Executable File

mod chunk_reader;
mod superblock;
use core::{fmt::Debug, mem::MaybeUninit};
use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
use limine::ModuleRequest;
use super::vfs::{FsOps, VNode, VNodeOperations, VNodeType};
pub static MODULE_REQUEST: ModuleRequest = ModuleRequest::new(0);
pub fn init() -> Squashfs<'static> {
// TODO: Put the module request stuff in another file?
if MODULE_REQUEST.get_response().get().is_none() {
panic!("Module request in none!");
}
let module_response = MODULE_REQUEST.get_response().get().unwrap();
let mut initramfs = None;
let module_name = "initramfs.img";
for module in module_response.modules() {
let c_path = module.path.to_str();
if c_path.is_none() {
continue;
}
if !c_path.unwrap().to_str().unwrap().contains(module_name) {
continue;
}
initramfs = Some(module);
}
// End TODO
if initramfs.is_none() {
panic!("Initramfs was not found!");
}
let initramfs = initramfs.unwrap();
let squashfs = Squashfs::new(initramfs.base.as_ptr().unwrap());
if squashfs.is_err() {
panic!("Initramfs in corrupt!");
}
let squashfs = squashfs.unwrap();
return squashfs;
}
#[repr(u8)]
#[derive(Clone, Copy)]
enum Table {
Inode,
Dir,
Frag,
Export,
ID,
Xattr,
}
#[repr(C)]
// #[derive(Debug)]
pub struct Squashfs<'a> {
pub superblock: superblock::SquashfsSuperblock,
start: *mut u8,
decompressor: Box<dyn Fn(&'a [u8]) -> Result<Vec<u8>, ()>>,
data_table: &'a [u8],
inode_table: chunk_reader::ChunkReader<'a, Box<dyn Fn(&[u8]) -> Result<Vec<u8>, ()>>>,
directory_table: chunk_reader::ChunkReader<'a, Box<dyn Fn(&[u8]) -> Result<Vec<u8>, ()>>>,
fragment_table: Option<&'a [u8]>,
export_table: Option<&'a [u8]>,
id_table: &'a [u8],
xattr_table: Option<&'a [u8]>,
}
impl Squashfs<'_> {
fn new(ptr: *mut u8) -> Result<Squashfs<'static>, ()> {
// crate::log_info!("Parsing initramfs at {:p}", ptr);
// 40 is the offset for bytes used by the archive in the superblock
let length = unsafe { u64::from_le(*(ptr.add(40) as *const u64)) as usize };
let squashfs_data: &[u8] = unsafe { core::slice::from_raw_parts(ptr, length) };
let superblock = superblock::SquashfsSuperblock::new(squashfs_data)?;
let decompressor = match superblock.compressor() {
superblock::SquashfsCompressionType::Gzip => {
Box::new(crate::libs::gzip::uncompress_data)
}
compressor => panic!("Unsupported SquashFS decompressor {compressor:?}"),
};
// The easy part with none of this metadata nonesense
let data_table = &squashfs_data[core::mem::size_of::<superblock::SquashfsSuperblock>()
..superblock.inode_table as usize];
let mut tables: Vec<(Table, u64)> = Vec::new();
// todo: there's probably a better way to do this
tables.push((Table::Inode, superblock.inode_table));
tables.push((Table::Dir, superblock.dir_table));
if superblock.frag_table != u64::MAX {
tables.push((Table::Frag, superblock.frag_table));
}
if superblock.export_table != u64::MAX {
tables.push((Table::Export, superblock.export_table));
}
tables.push((Table::ID, superblock.id_table));
if superblock.xattr_table != u64::MAX {
tables.push((Table::Xattr, superblock.xattr_table));
}
let mut inode_table: MaybeUninit<
chunk_reader::ChunkReader<'static, Box<dyn Fn(&[u8]) -> Result<Vec<u8>, ()>>>,
> = MaybeUninit::uninit();
let mut directory_table: MaybeUninit<
chunk_reader::ChunkReader<'static, Box<dyn Fn(&[u8]) -> Result<Vec<u8>, ()>>>,
> = MaybeUninit::uninit();
let mut fragment_table = None;
let mut export_table = None;
let mut id_table: &[u8] = &[];
let mut xattr_table = None;
for (i, &(table, offset)) in tables.iter().enumerate() {
let whole_table = if i == tables.len() - 1 {
&squashfs_data[offset as usize..]
} else {
&squashfs_data[offset as usize..tables[i + 1].1 as usize]
};
match table {
Table::Inode => {
inode_table = MaybeUninit::new(chunk_reader::ChunkReader::new(
whole_table,
decompressor.clone(),
));
}
Table::Dir => {
directory_table = MaybeUninit::new(chunk_reader::ChunkReader::new(
whole_table,
decompressor.clone(),
));
}
Table::Frag => {
fragment_table = Some(whole_table);
}
Table::Export => export_table = Some(whole_table),
Table::ID => id_table = whole_table,
Table::Xattr => xattr_table = Some(whole_table),
}
}
return Ok(Squashfs {
superblock,
start: ptr,
decompressor,
data_table,
inode_table: unsafe { inode_table.assume_init() },
directory_table: unsafe { directory_table.assume_init() },
fragment_table,
export_table,
id_table,
xattr_table,
});
}
fn get_inode_block_offset(&self, inode: u64) -> (u64, u16) {
let inode_block = ((inode >> 16) & 0x0000FFFFFFFFFFFF) as u64;
let inode_offset = (inode & 0xFFFF) as u16;
(inode_block, inode_offset)
}
fn read_root_dir(&mut self) -> Inode {
self.read_inode(self.superblock.root_inode)
}
fn read_inode(&mut self, inode: u64) -> Inode {
let (inode_block, inode_offset) = self.get_inode_block_offset(inode);
let file_type = InodeFileType::from(u16::from_le_bytes(
self.inode_table
.get_slice(inode_block, inode_offset, 2)
.try_into()
.unwrap(),
));
let inode_size = match file_type {
InodeFileType::BasicDirectory => core::mem::size_of::<BasicDirectoryInode>(),
InodeFileType::ExtendedDirectory => core::mem::size_of::<ExtendedDirectoryInode>(),
InodeFileType::BasicFile => core::mem::size_of::<BasicFileInode>(),
inode_type => unimplemented!("Inode type {inode_type:?}"),
};
let inode_bytes: &[u8] = &self
.inode_table
.get_slice(inode_block, inode_offset, inode_size);
Inode::from(inode_bytes)
}
fn find_entry_in_directory(&mut self, dir: Inode, name: &str) -> Result<Inode, ()> {
let dir_inode = match dir {
Inode::BasicDirectory(dir) => {
(dir.block_index as usize) << 16 | dir.block_offset as usize
}
Inode::ExtendedDirectory(dir) => {
(dir.block_index as usize) << 16 | dir.block_offset as usize
}
_ => return Err(()),
};
let dir_size = match dir {
Inode::BasicDirectory(dir) => dir.file_size as usize,
Inode::ExtendedDirectory(dir) => dir.file_size as usize,
_ => return Err(()),
};
if dir_size == 0 {
// directory has no entries
return Err(());
}
let (directory_block, directory_offset) = self.get_inode_block_offset(dir_inode as u64);
let mut directory_table_header = {
let bytes: &[u8] = &self.directory_table.get_slice(
directory_block,
directory_offset,
core::mem::size_of::<DirectoryTableHeader>(),
);
DirectoryTableHeader::from(bytes)
};
let mut offset = core::mem::size_of::<DirectoryTableHeader>();
let mut i = 0;
loop {
if i == directory_table_header.entry_count && offset != dir_size {
//read second table
directory_table_header = {
let bytes: &[u8] = &self.directory_table.get_slice(
directory_block,
directory_offset + offset as u16,
core::mem::size_of::<DirectoryTableHeader>(),
);
DirectoryTableHeader::from(bytes)
};
i = 0;
offset += core::mem::size_of::<DirectoryTableHeader>();
continue;
}
if offset >= dir_size {
break;
}
let name_size = u16::from_le_bytes(
self.directory_table
.get_slice(
directory_block,
directory_offset + (offset as u16 + 6),
2
)
.try_into()
.unwrap(),
) as usize
// the name is stored off-by-one
+ 1;
let directory_entry = DirectoryTableEntry::from_bytes(&self.directory_table.get_slice(
directory_block,
directory_offset + offset as u16,
8 + name_size,
));
offset += 8 + name_size;
if directory_entry.name == name {
let directory_entry_inode = (directory_table_header.start as usize) << 16
| (directory_entry.offset as usize);
return Ok(self.read_inode(directory_entry_inode as u64));
}
i += 1;
}
return Err(());
}
// metadata_block takes a tuple, the first element is whether the array is a metadata block,
// and the second element is a is_compressed override if the array is not a metadata block.
fn get_decompressed_table(
&self,
table: &[u8],
metadata_block: (bool, Option<bool>),
) -> Vec<u8> {
// the bottom 15 bits, I think the last bit indicates whether the data is uncompressed
let header = u16::from_le_bytes(table[0..2].try_into().unwrap());
let table_is_compressed = if !metadata_block.0 {
metadata_block.1.unwrap()
} else {
header & 0x8000 == 0
};
// let table_size = header & 0x7FFF;
// if table.len() >= 8192 {
// panic!("Inode block is not less than 8KiB!");
// }
let mut buffer: Vec<u8> = Vec::new();
let bytes = if metadata_block.0 { &table[2..] } else { table };
if table_is_compressed {
match self.superblock.compressor() {
superblock::SquashfsCompressionType::Gzip => {
buffer.extend_from_slice(
&crate::libs::gzip::uncompress_data(bytes).unwrap_or(bytes.to_vec()),
);
}
_ => {
crate::println!("Unsupported compression type")
}
}
} else {
buffer.extend(bytes);
}
return buffer;
}
}
impl<'a> FsOps for Squashfs<'a> {
fn mount(&mut self, _path: &str, data: &mut *mut u8, _vfspp: *const super::vfs::Vfs) {
// STUB
// not recommended:tm:
*data = core::ptr::addr_of!(*self) as *mut u8;
}
fn unmount(&mut self, _vfsp: *const super::vfs::Vfs) {
// STUB
}
fn root(&mut self, vfsp: *const super::vfs::Vfs) -> super::vfs::VNode {
let root_dir = self.read_root_dir();
super::vfs::VNode {
flags: 0,
ref_count: 0,
shared_lock_count: 0,
exclusive_lock_count: 0,
vfs_mounted_here: None,
ops: Box::new(root_dir),
node_data: None,
parent: vfsp,
typ: super::vfs::VNodeType::Directory,
data: core::ptr::null_mut(),
}
}
fn fid(&mut self, _path: &str, _vfspp: *const super::vfs::Vfs) -> Option<super::vfs::FileId> {
todo!();
}
fn statfs(&mut self, _vfsp: *const super::vfs::Vfs) -> super::vfs::StatFs {
todo!();
}
fn sync(&mut self, _vfsp: *const super::vfs::Vfs) {
todo!();
}
fn vget(
&mut self,
_fid: super::vfs::FileId,
_vfsp: *const super::vfs::Vfs,
) -> super::vfs::VNode {
todo!();
}
}
#[derive(Clone, Copy, Debug)]
enum Inode {
BasicFile(BasicFileInode),
BasicDirectory(BasicDirectoryInode),
ExtendedDirectory(ExtendedDirectoryInode),
}
impl From<&[u8]> for Inode {
fn from(value: &[u8]) -> Self {
let file_type = InodeFileType::from(u16::from_le_bytes(value[0..2].try_into().unwrap()));
match file_type {
InodeFileType::BasicDirectory => {
Inode::BasicDirectory(BasicDirectoryInode::from_bytes(value))
}
InodeFileType::ExtendedDirectory => {
Inode::ExtendedDirectory(ExtendedDirectoryInode::from_bytes(value))
}
InodeFileType::BasicFile => Inode::BasicFile(BasicFileInode::from_bytes(value)),
_ => unimplemented!("Inode from bytes"),
}
}
}
impl VNodeOperations for Inode {
fn open(
&mut self,
_f: u32,
_c: super::vfs::UserCred,
vp: *const VNode,
) -> Result<Arc<[u8]>, ()> {
let squashfs = unsafe { (*(*vp).parent).data.cast::<Squashfs>() };
match self {
Inode::BasicFile(file) => unsafe {
// TODO: is this really how you're supposed to do this?
let mut block_data: Vec<u8> = Vec::with_capacity(file.file_size as usize);
let data_table: Vec<u8>;
let block_offset = if file.frag_idx == u32::MAX {
data_table = (*squashfs).get_decompressed_table(
(*squashfs).data_table,
(
false,
Some(!(*squashfs).superblock.features().uncompressed_data_blocks),
),
);
file.block_offset as usize
} else {
// Tail end packing
let fragment_table = (*squashfs).get_decompressed_table(
(*squashfs).fragment_table.unwrap(),
(
false,
Some(!(*squashfs).superblock.features().uncompressed_fragments),
),
);
let fragment_pointer = ((*squashfs).start as u64
+ u64::from_le_bytes(
fragment_table[file.frag_idx as usize..(file.frag_idx + 8) as usize]
.try_into()
.unwrap(),
)) as *mut u8;
// build array since fragment_pointer is not guaranteed to be 0x02 aligned
// We add two since fragment_pointer points to the beginning of the fragment block,
// Which is a metadata block, and we get the size, but that excludes the two header bytes,
// And since we are building the array due to unaligned pointer shenanigans we need to
// include the header bytes otherwise we are short by two bytes
let fragment_block_size =
(u16::from_le(core::ptr::read_unaligned(fragment_pointer as *mut u16))
& 0x7FFF)
+ 2;
let mut fragment_block_raw = Vec::new();
for i in 0..fragment_block_size as usize {
fragment_block_raw.push(core::ptr::read_unaligned(fragment_pointer.add(i)))
}
let fragment_block =
(*squashfs).get_decompressed_table(&fragment_block_raw, (true, None));
let fragment_start =
u64::from_le_bytes(fragment_block[0..8].try_into().unwrap());
let fragment_size =
u32::from_le_bytes(fragment_block[8..12].try_into().unwrap());
let fragment_compressed = fragment_size & 1 << 24 == 0;
let fragment_size = fragment_size & 0xFEFFFFFF;
let data_table_raw = core::slice::from_raw_parts(
((*squashfs).start as u64 + fragment_start) as *mut u8,
fragment_size as usize,
)
.to_vec();
data_table = (*squashfs).get_decompressed_table(
&data_table_raw,
(false, Some(fragment_compressed)),
);
file.block_offset as usize
};
block_data
.extend(&data_table[block_offset..(block_offset + file.file_size as usize)]);
return Ok(Arc::from(block_data));
},
_ => panic!("Tried to open non-file"),
}
}
fn close(&mut self, _f: u32, _c: super::vfs::UserCred, _vp: *const VNode) {
todo!()
}
fn rdwr(
&mut self,
_uiop: *const super::vfs::UIO,
_direction: super::vfs::IODirection,
_f: u32,
_c: super::vfs::UserCred,
_vp: *const VNode,
) {
todo!()
}
fn ioctl(
&mut self,
_com: u32,
_d: *mut u8,
_f: u32,
_c: super::vfs::UserCred,
_vp: *const VNode,
) {
todo!()
}
fn select(&mut self, _w: super::vfs::IODirection, _c: super::vfs::UserCred, _vp: *const VNode) {
todo!()
}
fn getattr(&mut self, _c: super::vfs::UserCred, _vp: *const VNode) -> super::vfs::VAttr {
todo!()
}
fn setattr(&mut self, _va: super::vfs::VAttr, _c: super::vfs::UserCred, _vp: *const VNode) {
todo!()
}
fn access(&mut self, _m: u32, _c: super::vfs::UserCred, _vp: *const VNode) {
todo!()
}
fn lookup(
&mut self,
nm: &str,
_c: super::vfs::UserCred,
vp: *const VNode,
) -> Result<super::vfs::VNode, ()> {
let squashfs = unsafe { (*(*vp).parent).data.cast::<Squashfs>() };
match self {
Inode::BasicDirectory(_) | Inode::ExtendedDirectory(_) => unsafe {
let inode = (*squashfs).find_entry_in_directory(*self, nm)?;
let vnode_type = match inode {
Inode::BasicDirectory(_) | Inode::ExtendedDirectory(_) => VNodeType::Directory,
Inode::BasicFile(_) => VNodeType::Regular,
};
let vnode = VNode {
flags: 0,
ref_count: 0,
shared_lock_count: 0,
exclusive_lock_count: 0,
vfs_mounted_here: None,
ops: Box::new(inode),
node_data: None,
parent: (*vp).parent,
typ: vnode_type,
data: core::ptr::null_mut(),
};
return Ok(vnode);
},
_ => panic!("tried to lookup on non directory"),
}
}
fn create(
&mut self,
_nm: &str,
_va: super::vfs::VAttr,
_e: u32,
_m: u32,
_c: super::vfs::UserCred,
_vp: *const VNode,
) -> Result<super::vfs::VNode, ()> {
todo!()
}
fn link(
&mut self,
_target_dir: *mut super::vfs::VNode,
_target_name: &str,
_c: super::vfs::UserCred,
_vp: *const VNode,
) {
todo!()
}
fn rename(
&mut self,
_nm: &str,
_target_dir: *mut super::vfs::VNode,
_target_name: &str,
_c: super::vfs::UserCred,
_vp: *const VNode,
) {
todo!()
}
fn mkdir(
&mut self,
_nm: &str,
_va: super::vfs::VAttr,
_c: super::vfs::UserCred,
_vp: *const VNode,
) -> Result<super::vfs::VNode, ()> {
todo!()
}
fn readdir(
&mut self,
_uiop: *const super::vfs::UIO,
_c: super::vfs::UserCred,
_vp: *const VNode,
) {
todo!()
}
fn symlink(
&mut self,
_link_name: &str,
_va: super::vfs::VAttr,
_target_name: &str,
_c: super::vfs::UserCred,
_vp: *const VNode,
) {
todo!()
}
fn readlink(
&mut self,
_uiop: *const super::vfs::UIO,
_c: super::vfs::UserCred,
_vp: *const VNode,
) {
todo!()
}
fn fsync(&mut self, _c: super::vfs::UserCred, _vp: *const VNode) {
todo!()
}
fn inactive(&mut self, _c: super::vfs::UserCred, _vp: *const VNode) {
todo!()
}
fn bmap(&mut self, _block_number: u32, _bnp: (), _vp: *const VNode) -> super::vfs::VNode {
todo!()
}
fn strategy(&mut self, _bp: (), _vp: *const VNode) {
todo!()
}
fn bread(&mut self, _block_number: u32, _vp: *const VNode) -> Arc<[u8]> {
todo!()
}
}
macro_rules! inode_enum_try_into {
($inode_type:ty, $inode_name:ident) => {
impl<'a> TryInto<$inode_type> for Inode {
type Error = ();
fn try_into(self) -> Result<$inode_type, Self::Error> {
match self {
Inode::$inode_name(inode) => {
return Ok(inode);
}
_ => {
return Err(());
}
}
}
}
};
}
inode_enum_try_into!(BasicFileInode, BasicFile);
inode_enum_try_into!(BasicDirectoryInode, BasicDirectory);
inode_enum_try_into!(ExtendedDirectoryInode, ExtendedDirectory);
#[repr(C)]
#[derive(Clone, Copy, Debug)]
struct InodeHeader {
file_type: InodeFileType,
_reserved: [u16; 3],
mtime: u32,
inode_num: u32,
}
impl InodeHeader {
fn from_bytes(bytes: &[u8]) -> Self {
let file_type = u16::from_le_bytes(bytes[0..2].try_into().unwrap()).into();
let mtime = u32::from_le_bytes(bytes[8..12].try_into().unwrap());
let inode_num = u32::from_le_bytes(bytes[12..16].try_into().unwrap());
return Self {
// squashfs,
file_type,
_reserved: [0; 3],
mtime,
inode_num,
};
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
struct BasicDirectoryInode {
header: InodeHeader,
block_index: u32, // 4
link_count: u32, // 8
file_size: u16, // 10
block_offset: u16, // 12
parent_inode: u32, // 16
}
impl BasicDirectoryInode {
fn from_bytes(bytes: &[u8]) -> Self {
let header = InodeHeader::from_bytes(bytes);
let block_index = u32::from_le_bytes(bytes[16..20].try_into().unwrap());
let link_count = u32::from_le_bytes(bytes[20..24].try_into().unwrap());
let file_size = u16::from_le_bytes(bytes[24..26].try_into().unwrap());
let block_offset = u16::from_le_bytes(bytes[26..28].try_into().unwrap());
let parent_inode = u32::from_le_bytes(bytes[28..32].try_into().unwrap());
return Self {
header,
block_index,
link_count,
file_size,
block_offset,
parent_inode,
};
}
// #[allow(dead_code)]
// fn entries(&self) -> Arc<[Inode]> {
// let mut entries: Vec<Inode> = Vec::new();
// let directory_table = &self
// .header
// .squashfs
// .get_decompressed_table(self.header.squashfs.directory_table, (true, None));
// let directory_table_header =
// DirectoryTableHeader::from_bytes(&directory_table[self.block_offset as usize..]);
// // TODO: cheap hack, fix it when I have more hours of sleep.
// let mut offset = self.block_offset as usize + core::mem::size_of::<DirectoryTableHeader>();
// for _ in 0..directory_table_header.entry_count as usize {
// let directory_table_entry = DirectoryTableEntry::from_bytes(&directory_table[offset..]);
// offset += 8 + directory_table_entry.name.len();
// let file_inode = self
// .header
// .squashfs
// .read_inode(directory_table_entry.offset as u32);
// entries.push(file_inode);
// }
// return Arc::from(entries);
// }
// fn find(&self, name: &str) -> Option<Inode<'a>> {
// let directory_table = &self
// .header
// .squashfs
// .get_decompressed_table(self.header.squashfs.directory_table, (true, None));
// let directory_table_header =
// DirectoryTableHeader::from_bytes(&directory_table[self.block_offset as usize..]);
// // TODO: cheap hack, fix it when I have more hours of sleep.
// let mut offset = self.block_offset as usize + core::mem::size_of::<DirectoryTableHeader>();
// InodeHeader
// for _ in 0..directory_table_header.entry_count as usize {
// let directory_table_entry = DirectoryTableEntry::from_bytes(&directory_table[offset..]);
// offset += 8 + directory_table_entry.name.len();
// if directory_table_entry.name == name {
// return Some(
// self.header
// .squashfs
// .read_inode(directory_table_entry.offset as u32),
// );
// }
// }
// return None;
// }
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
struct ExtendedDirectoryInode {
header: InodeHeader,
link_count: u32, // 8
file_size: u32, // 10
block_index: u32, // 4
parent_inode: u32, // 16
index_count: u16,
block_offset: u16, // 12
xattr_index: u32,
}
impl ExtendedDirectoryInode {
fn from_bytes(bytes: &[u8]) -> Self {
let header = InodeHeader::from_bytes(bytes);
let link_count = u32::from_le_bytes(bytes[16..20].try_into().unwrap());
let file_size = u32::from_le_bytes(bytes[20..24].try_into().unwrap());
let block_index = u32::from_le_bytes(bytes[24..28].try_into().unwrap());
let parent_inode = u32::from_le_bytes(bytes[28..32].try_into().unwrap());
let index_count = u16::from_le_bytes(bytes[32..34].try_into().unwrap());
let block_offset = u16::from_le_bytes(bytes[34..36].try_into().unwrap());
let xattr_index = u32::from_le_bytes(bytes[36..40].try_into().unwrap());
return Self {
header,
link_count,
file_size,
block_index,
parent_inode,
index_count,
block_offset,
xattr_index,
};
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
struct BasicFileInode {
header: InodeHeader,
block_start: u32, // 4
frag_idx: u32, // 8
block_offset: u32, // 12
file_size: u32, // 16
// block_sizes: *const u32,
}
impl BasicFileInode {
fn from_bytes(bytes: &[u8]) -> Self {
let header = InodeHeader::from_bytes(bytes);
let block_start = u32::from_le_bytes(bytes[16..20].try_into().unwrap());
let frag_idx = u32::from_le_bytes(bytes[20..24].try_into().unwrap());
let block_offset = u32::from_le_bytes(bytes[24..28].try_into().unwrap());
let file_size = u32::from_le_bytes(bytes[28..32].try_into().unwrap());
// let block_sizes = bytes[32..].as_ptr() as *const u32;
return Self {
header,
block_start,
frag_idx,
block_offset,
file_size,
// block_sizes,
};
}
}
#[repr(C)]
#[derive(Debug)]
struct DirectoryTableHeader {
entry_count: u32,
start: u32,
inode_num: u32,
}
impl From<&[u8]> for DirectoryTableHeader {
fn from(value: &[u8]) -> Self {
// count is off by 1 entry
let entry_count = u32::from_le_bytes(value[0..4].try_into().unwrap()) + 1;
let start = u32::from_le_bytes(value[4..8].try_into().unwrap());
let inode_num = u32::from_le_bytes(value[8..12].try_into().unwrap());
return Self {
entry_count,
start,
inode_num,
};
}
}
#[repr(C)]
#[derive(Debug)]
struct DirectoryTableEntry {
offset: u16,
inode_offset: i16,
inode_type: InodeFileType,
name_size: u16,
name: String, // the file name length is name_size + 1 bytes
}
impl DirectoryTableEntry {
fn from_bytes(bytes: &[u8]) -> Self {
let offset = u16::from_le_bytes(bytes[0..2].try_into().unwrap());
let inode_offset = i16::from_le_bytes(bytes[2..4].try_into().unwrap());
let inode_type = u16::from_le_bytes(bytes[4..6].try_into().unwrap()).into();
let name_size = u16::from_le_bytes(bytes[6..8].try_into().unwrap());
let name = String::from_utf8(bytes[8..((name_size as usize) + 1) + 8].to_vec()).unwrap();
// let name = core::str::from_utf8(&bytes[8..((name_size as usize) + 1) + 8])
// .expect("Failed to make DirectoryHeader name");
return Self {
offset,
inode_offset,
inode_type,
name_size,
name,
};
}
}
#[repr(u16)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum InodeFileType {
BasicDirectory = 1,
BasicFile = 2,
BasicSymlink = 3,
BasicBlockDevice = 4,
BasicCharDevice = 5,
BasicPipe = 6,
BasicSocked = 7,
ExtendedDirectory = 8,
ExtendedFile = 9,
ExtendedSymlink = 10,
ExtendedBlockDevice = 11,
ExtendedPipe = 12,
ExtendedSocked = 13,
}
impl From<u16> for InodeFileType {
fn from(value: u16) -> Self {
match value {
1 => Self::BasicDirectory,
2 => Self::BasicFile,
3 => Self::BasicSymlink,
4 => Self::BasicBlockDevice,
5 => Self::BasicCharDevice,
6 => Self::BasicPipe,
7 => Self::BasicSocked,
8 => Self::ExtendedDirectory,
9 => Self::ExtendedFile,
10 => Self::ExtendedSymlink,
11 => Self::ExtendedBlockDevice,
12 => Self::ExtendedPipe,
13 => Self::ExtendedSocked,
_ => panic!("Unexpected Inode file type {value}!"),
}
}
}