ableos/ext2-rs/src/fs/mod.rs

178 lines
4.5 KiB
Rust

//!
use {
alloc::vec::Vec,
core::mem,
error::Error,
sector::{Address, SectorSize},
sys::{
block_group::BlockGroupDescriptor, inode::Inode as RawInode,
superblock::Superblock,
},
volume::Volume,
};
pub mod sync;
#[allow(dead_code)]
pub(crate) struct Struct<T, S: SectorSize> {
pub inner: T,
pub offset: Address<S>,
}
impl<T, S: SectorSize> From<(T, Address<S>)> for Struct<T, S> {
#[inline]
fn from((inner, offset): (T, Address<S>)) -> Struct<T, S> {
Struct { inner, offset }
}
}
/// Safe wrapper for raw sys structs
pub struct Ext2<S: SectorSize, V: Volume<u8, S>> {
// TODO: should this have some different vis?
pub(crate) volume: V,
pub(crate) superblock: Struct<Superblock, S>,
pub(crate) block_groups: Struct<Vec<BlockGroupDescriptor>, S>,
}
impl<S: SectorSize, V: Volume<u8, S>> Ext2<S, V> {
///
pub fn new(volume: V) -> Result<Ext2<S, V>, Error> {
let superblock = unsafe { Struct::from(Superblock::find(&volume)?) };
let block_groups_offset = Address::with_block_size(
superblock.inner.first_data_block + 1,
0,
superblock.inner.log_block_size + 10,
);
let block_groups_count = superblock
.inner
.block_group_count()
.map(|count| count as usize)
.map_err(|(a, b)| Error::BadBlockGroupCount {
by_blocks: a,
by_inodes: b,
})?;
let block_groups = unsafe {
BlockGroupDescriptor::find_descriptor_table(
&volume,
block_groups_offset,
block_groups_count,
)?
};
let block_groups = Struct::from(block_groups);
Ok(Ext2 {
volume,
superblock,
block_groups,
})
}
/// Return the version of the filesystem
pub fn version(&self) -> (u32, u16) {
(
self.superblock.inner.rev_major,
self.superblock.inner.rev_minor,
)
}
/// Return inode size
pub fn inode_size(&self) -> usize {
if self.version().0 == 0 {
mem::size_of::<RawInode>()
} else {
// note: inodes bigger than 128 are not supported
self.superblock.inner.inode_size as usize
}
}
///
pub fn inodes_count(&self) -> usize {
self.superblock.inner.inodes_per_group as _
}
///
pub fn total_inodes_count(&self) -> usize {
self.superblock.inner.inodes_count as _
}
///
pub fn block_group_count(&self) -> Result<usize, Error> {
self.superblock
.inner
.block_group_count()
.map(|count| count as usize)
.map_err(|(a, b)| Error::BadBlockGroupCount {
by_blocks: a,
by_inodes: b,
})
}
///
pub fn total_block_count(&self) -> usize {
self.superblock.inner.blocks_count as _
}
///
pub fn free_block_count(&self) -> usize {
self.superblock.inner.free_blocks_count as _
}
///
pub fn block_size(&self) -> usize {
self.superblock.inner.block_size()
}
///
pub fn log_block_size(&self) -> u32 {
self.superblock.inner.log_block_size + 10
}
///
pub fn sector_size(&self) -> usize {
S::SIZE
}
///
pub fn log_sector_size(&self) -> u32 {
S::LOG_SIZE
}
}
#[cfg(test)]
mod tests {
use std::cell::RefCell;
use std::fs::File;
use sector::{Address, Size512};
use volume::Volume;
use super::Ext2;
#[test]
fn file_len() {
let file = RefCell::new(File::open("ext2.img").unwrap());
assert_eq!(
Address::<Size512>::from(2048_u64)
- Address::<Size512>::from(1024_u64),
Address::<Size512>::new(2, 0)
);
assert_eq!(
unsafe {
file.slice_unchecked(
Address::<Size512>::from(1024_u64)
..Address::<Size512>::from(2048_u64),
)
.len()
},
1024
);
}
#[test]
fn file() {
let file = RefCell::new(File::open("ext2.img").unwrap());
let fs = Ext2::<Size512, _>::new(file);
assert!(
fs.is_ok(),
"Err({:?})",
fs.err().unwrap_or_else(|| unreachable!()),
);
let fs = fs.unwrap();
let vers = fs.version();
println!("version: {}.{}", vers.0, vers.1);
assert_eq!(128, fs.inode_size());
}
}