225 lines
5.8 KiB
Rust
225 lines
5.8 KiB
Rust
|
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");
|
||
|
}
|
||
|
}
|