225 lines
5.8 KiB
Rust
Raw Normal View History

2024-12-02 19:02:18 +02:00
use core::{cmp::Ordering, ops::Deref};
use alloc::sync::{Arc, Weak};
use libk::{
error::Error,
task::sync::{AsyncMutex, MappedAsyncMutexGuard, Mutex},
};
use libk_util::{
lru_hash_table::LruCache,
sync::spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockReadGuard, IrqSafeRwLockWriteGuard},
};
use crate::{
data::{FsReadonlyFeatures, DIRECT_BLOCK_COUNT},
Ext2Fs, Inode,
};
struct InodeHolder {
inode: Inode,
dirty: bool,
}
pub struct InodeRef {
_entry: Arc<IrqSafeRwLock<InodeHolder>>,
lock: IrqSafeRwLockReadGuard<'static, InodeHolder>,
}
pub struct InodeMut {
_entry: Arc<IrqSafeRwLock<InodeHolder>>,
lock: IrqSafeRwLockWriteGuard<'static, InodeHolder>,
}
pub struct InodeCache {
fs: Arc<Ext2Fs>,
cache: AsyncMutex<LruCache<u32, Arc<IrqSafeRwLock<InodeHolder>>>>,
}
impl InodeCache {
pub fn with_capacity(fs: Arc<Ext2Fs>, bucket_capacity: usize) -> Self {
Self {
fs,
cache: AsyncMutex::new(LruCache::with_capacity(bucket_capacity, 4)),
}
}
async fn evict_inode(&self, ino: u32, inode: Arc<IrqSafeRwLock<InodeHolder>>) {
let inode = inode.read();
if inode.dirty {
log::debug!("Flush dirty inode {ino}");
todo!();
}
}
async fn fetch_inode(&self, ino: u32) -> Result<Arc<IrqSafeRwLock<InodeHolder>>, Error> {
let inode = self.fs.read_inode(ino).await?;
log::error!("InodeHolder created");
Ok(Arc::new(IrqSafeRwLock::new(InodeHolder {
inode,
dirty: false,
})))
}
async fn entry<'a>(
&'a self,
ino: u32,
) -> Result<
MappedAsyncMutexGuard<
'a,
Arc<IrqSafeRwLock<InodeHolder>>,
LruCache<u32, Arc<IrqSafeRwLock<InodeHolder>>>,
>,
Error,
> {
if ino < 1 || ino > self.fs.total_inodes {
return Err(Error::InvalidFile);
}
let key = ino - 1;
self.cache
.lock()
.await
.try_map_guard_async(|cache: &'a mut LruCache<_, _>| async move {
let (value, evicted) = cache
.try_get_or_insert_with_async(key, || self.fetch_inode(ino))
.await?;
if let Some((ino, holder)) = evicted {
self.evict_inode(ino, holder).await;
}
Ok(value)
})
.await
}
pub async fn get(&self, ino: u32) -> Result<InodeRef, Error> {
self.entry(ino).await.map(|e| InodeRef::new(ino, e.deref()))
}
pub async fn get_mut(&self, ino: u32) -> Result<InodeMut, Error> {
self.entry(ino).await.map(|e| InodeMut::new(ino, e.deref()))
}
}
impl InodeRef {
fn new(ino: u32, entry: &Arc<IrqSafeRwLock<InodeHolder>>) -> Self {
let entry = entry.clone();
// Safety: ok, Arc instance is still held
let lock = unsafe { core::mem::transmute(entry.read()) };
Self {
lock,
_entry: entry,
}
}
}
impl Deref for InodeRef {
type Target = Inode;
fn deref(&self) -> &Self::Target {
&self.lock.inode
}
}
impl InodeMut {
fn new(ino: u32, entry: &Arc<IrqSafeRwLock<InodeHolder>>) -> Self {
let entry = entry.clone();
// Safety: ok, Arc instance is still held
let lock = unsafe { core::mem::transmute(entry.write()) };
Self {
lock,
_entry: entry,
}
}
}
impl InodeMut {
async fn grow_direct(
&mut self,
fs: &Ext2Fs,
old_capacity: u64,
new_capacity: u64,
) -> Result<(), Error> {
let old_l0_capacity = old_capacity.min(DIRECT_BLOCK_COUNT as u64);
let new_l0_capacity = new_capacity.min(DIRECT_BLOCK_COUNT as u64);
debug_assert!(old_l0_capacity <= new_l0_capacity);
for i in old_l0_capacity..new_l0_capacity {
let i = i as usize;
let block = fs.allocate_block().await?;
self.lock.inode.blocks.direct_blocks[i] = block;
self.lock.dirty = true;
}
Ok(())
}
pub async fn resize(&mut self, fs: &Ext2Fs, size: u64) -> Result<(), Error> {
if size == self.size(fs) {
return Ok(());
}
let new_blocks = size.div_ceil(fs.block_size as u64);
let old_blocks = self.size(&fs).div_ceil(fs.block_size as u64);
match old_blocks.cmp(&new_blocks) {
// Grow
Ordering::Less => {
if new_blocks > DIRECT_BLOCK_COUNT as u64 {
todo!();
}
log::debug!("Grow inode: {old_blocks} -> {new_blocks} blocks");
self.grow_direct(fs, old_blocks, new_blocks).await?;
}
// Shrink
Ordering::Greater => todo!(),
// No change
Ordering::Equal => (),
}
if fs
.write_features
.contains(FsReadonlyFeatures::FILE_SIZE_64_BIT)
{
self.lock.inode.size_upper = (size >> 32) as u32;
self.lock.inode.size_lower = size as u32;
} else {
if size > u32::MAX as u64 {
todo!("File too large")
}
self.lock.inode.size_lower = size as u32;
}
self.lock.dirty = true;
Ok(())
}
pub async fn reserve(&mut self, fs: &Ext2Fs, capacity: u64) -> Result<(), Error> {
if capacity > self.size(fs) {
self.resize(fs, capacity).await?;
}
Ok(())
}
pub fn inc_hard_count(&mut self) {
self.lock.inode.hard_links += 1;
self.lock.dirty = true;
}
}
impl Deref for InodeMut {
type Target = Inode;
fn deref(&self) -> &Self::Target {
&self.lock.inode
}
}
impl Drop for InodeHolder {
fn drop(&mut self) {
log::error!("InodeHolder dropped");
}
}