ext2: use the same access method for icache as for bcache
This commit is contained in:
parent
5f2c99f5c7
commit
fd9ea77adb
@ -113,6 +113,12 @@ impl BlockDevice for NvmeNamespace {
|
||||
debug_assert_eq!(buffer_address.into_u64() % self.block_size() as u64, 0);
|
||||
let lba_count = buffer.len() / self.block_size();
|
||||
|
||||
// TODO ArchitectureImpl::flush_data_cache()
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
unsafe {
|
||||
core::arch::asm!("wbinvd");
|
||||
}
|
||||
|
||||
let result = self
|
||||
.controller
|
||||
.perform_io(
|
||||
|
118
kernel/driver/fs/ext2/src/allocation.rs
Normal file
118
kernel/driver/fs/ext2/src/allocation.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use libk::error::Error;
|
||||
|
||||
use crate::{BlockGroupDescriptor, Ext2Fs, ExtendedSuperblock};
|
||||
|
||||
impl Ext2Fs {
|
||||
async fn allocate<
|
||||
F: Fn(&mut BlockGroupDescriptor) -> Option<(u32, u32)>,
|
||||
G: Fn(&mut ExtendedSuperblock),
|
||||
>(
|
||||
&self,
|
||||
descriptor_mapper: F,
|
||||
superblock_mapper: G,
|
||||
) -> Result<u32, Error> {
|
||||
let bit_per_block = self.block_size * 8;
|
||||
for group_index in 0..self.bgdt.entry_count as u32 {
|
||||
let bitmap = self
|
||||
.with_bgdt_entry_mut(group_index, |descriptor| {
|
||||
match descriptor_mapper(descriptor) {
|
||||
Some(val) => Ok(Some(val)),
|
||||
_ => Ok(None),
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
if let Some((bitmap, group_item_count)) = bitmap {
|
||||
let no = self
|
||||
.with_block_mut(bitmap, 0, |bitmap| {
|
||||
for i in 0..bit_per_block.min(group_item_count as usize) {
|
||||
let index = i / 8;
|
||||
let bit = 1u8 << (i % 8);
|
||||
|
||||
if bitmap[index] & bit == 0 {
|
||||
bitmap[index] |= bit;
|
||||
return Ok(Some(i as u32));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
})
|
||||
.await?
|
||||
.expect("TODO: bgdt says there're things, but bitmap says there aren't");
|
||||
|
||||
{
|
||||
let mut state = self.state.write();
|
||||
superblock_mapper(&mut state.superblock);
|
||||
state.dirty = true;
|
||||
}
|
||||
|
||||
if !self.cached {
|
||||
self.flush_superblock().await?;
|
||||
}
|
||||
|
||||
return Ok(group_index * group_item_count + no);
|
||||
}
|
||||
}
|
||||
|
||||
todo!("ENOSPC")
|
||||
}
|
||||
|
||||
pub(crate) async fn allocate_inode(&self, directory: bool) -> Result<u32, Error> {
|
||||
let ino = self
|
||||
.allocate(
|
||||
|descriptor| {
|
||||
if descriptor.unallocated_inodes == 0
|
||||
|| (directory && descriptor.directories == u16::MAX)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
if directory {
|
||||
descriptor.directories += 1;
|
||||
}
|
||||
descriptor.unallocated_inodes -= 1;
|
||||
log::info!("bg unallocated_inodes = {}", descriptor.unallocated_inodes);
|
||||
Some((descriptor.inode_usage_bitmap, self.block_group_inode_count))
|
||||
}
|
||||
},
|
||||
|superblock| {
|
||||
superblock.total_unallocated_inodes -= 1;
|
||||
log::info!(
|
||||
"total_unallocated_inodes = {}",
|
||||
superblock.total_unallocated_inodes
|
||||
);
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
let ino = ino + 1;
|
||||
|
||||
log::debug!("ext2: allocated inode #{ino}");
|
||||
|
||||
Ok(ino)
|
||||
}
|
||||
|
||||
pub(crate) async fn allocate_block(&self) -> Result<u32, Error> {
|
||||
let no = self
|
||||
.allocate(
|
||||
|descriptor| {
|
||||
if descriptor.unallocated_blocks == 0 {
|
||||
None
|
||||
} else {
|
||||
descriptor.unallocated_blocks -= 1;
|
||||
log::info!("bg unallocated_blocks = {}", descriptor.unallocated_blocks);
|
||||
Some((descriptor.block_usage_bitmap, self.block_group_block_count))
|
||||
}
|
||||
},
|
||||
|superblock| {
|
||||
superblock.total_unallocated_blocks -= 1;
|
||||
log::info!(
|
||||
"total_unallocated_blocks = {}",
|
||||
superblock.total_unallocated_blocks
|
||||
);
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
log::debug!("ext2: allocated block #{no}");
|
||||
|
||||
Ok(no)
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ use libk::{
|
||||
vfs::{CommonImpl, DirectoryImpl, DirectoryOpenPosition, Metadata, Node, NodeFlags, NodeRef},
|
||||
};
|
||||
use yggdrasil_abi::{
|
||||
io::{DirectoryEntry, FileType},
|
||||
io::{DirectoryEntry, FileMode, FileType},
|
||||
util::FixedString,
|
||||
};
|
||||
|
||||
@ -40,8 +40,9 @@ struct DirentIterMut<'a> {
|
||||
|
||||
struct Record<'a> {
|
||||
fs: &'a Ext2Fs,
|
||||
offset: usize,
|
||||
data: &'a mut [u8],
|
||||
#[allow(unused)]
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl Record<'_> {
|
||||
@ -57,9 +58,9 @@ impl Record<'_> {
|
||||
self.dirent().ino == 0
|
||||
}
|
||||
|
||||
pub fn ino(&self) -> u32 {
|
||||
self.dirent().ino
|
||||
}
|
||||
// pub fn ino(&self) -> u32 {
|
||||
// self.dirent().ino
|
||||
// }
|
||||
|
||||
fn name_len(&self) -> usize {
|
||||
let len = self.dirent().name_length_low as usize;
|
||||
@ -81,26 +82,26 @@ impl Record<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn name(&self) -> Option<&str> {
|
||||
core::str::from_utf8(self.name_bytes()).ok()
|
||||
}
|
||||
// fn name(&self) -> Option<&str> {
|
||||
// core::str::from_utf8(self.name_bytes()).ok()
|
||||
// }
|
||||
|
||||
pub fn data_size(&self) -> usize {
|
||||
// Whole dirent is free space
|
||||
if self.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
// pub fn data_size(&self) -> usize {
|
||||
// // Whole dirent is free space
|
||||
// if self.is_empty() {
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
self.name_len() + size_of::<Dirent>()
|
||||
}
|
||||
// self.name_len() + size_of::<Dirent>()
|
||||
// }
|
||||
|
||||
pub fn record_size(&self) -> usize {
|
||||
(self.dirent().ent_size as usize).max(size_of::<Dirent>())
|
||||
}
|
||||
// pub fn record_size(&self) -> usize {
|
||||
// (self.dirent().ent_size as usize).max(size_of::<Dirent>())
|
||||
// }
|
||||
|
||||
pub fn free_space(&self) -> usize {
|
||||
self.data.len().saturating_sub(self.data_size())
|
||||
}
|
||||
// pub fn free_space(&self) -> usize {
|
||||
// self.data.len().saturating_sub(self.data_size())
|
||||
// }
|
||||
}
|
||||
|
||||
impl<'a> DirentIterMut<'a> {
|
||||
@ -300,120 +301,114 @@ impl DirectoryNode {
|
||||
return Err(Error::DoesNotExist);
|
||||
}
|
||||
|
||||
{
|
||||
let mut holder = self.inode.cache().get_mut(ino).await?;
|
||||
{
|
||||
let mut child = holder.write();
|
||||
let child = InodeAccess::new(self.inode.cache().clone(), ino);
|
||||
child
|
||||
.map_mut(|inode| {
|
||||
inode.inc_hard_count();
|
||||
inode.dtime = 0;
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
child.inc_hard_count();
|
||||
child.dtime = 0;
|
||||
}
|
||||
self.inode
|
||||
.amap_mut(async |inode| {
|
||||
let mut fit = false;
|
||||
let n = inode.blocks(&self.fs) as u32;
|
||||
for i in 0..n {
|
||||
let fit_block = self
|
||||
.fs
|
||||
.with_inode_block_mut(&inode, i, 0, |block| {
|
||||
let mut iter = DirentIterMut::new(&self.fs, &mut block[..], 0);
|
||||
if iter.try_fit(name, ino) {
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
holder.put().await?;
|
||||
}
|
||||
|
||||
let mut holder = self.inode.get_mut().await?;
|
||||
{
|
||||
let mut inode = holder.write();
|
||||
let mut fit = false;
|
||||
let n = inode.blocks(&self.fs) as u32;
|
||||
for i in 0..n {
|
||||
let fit_block = self
|
||||
.fs
|
||||
.with_inode_block_mut(&inode, i, 0, |block| {
|
||||
let mut iter = DirentIterMut::new(&self.fs, &mut block[..], 0);
|
||||
if iter.try_fit(name, ino) {
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
if fit_block {
|
||||
fit = true;
|
||||
break;
|
||||
if fit_block {
|
||||
fit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !fit {
|
||||
// Allocate a new block
|
||||
let block_index = inode.blocks(&self.fs) as u32;
|
||||
if !fit {
|
||||
// Allocate a new block
|
||||
let block_index = inode.blocks(&self.fs) as u32;
|
||||
|
||||
// Grow the storage
|
||||
inode
|
||||
.reserve(
|
||||
&self.fs,
|
||||
(block_index as u64 + 1) * self.fs.block_size as u64,
|
||||
)
|
||||
.await?;
|
||||
// Grow the storage
|
||||
inode
|
||||
.reserve(
|
||||
&self.fs,
|
||||
(block_index as u64 + 1) * self.fs.block_size as u64,
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.fs
|
||||
.with_inode_block_mut(&inode, block_index, self.fs.block_size, |block| {
|
||||
block.fill(0);
|
||||
self.fs
|
||||
.with_inode_block_mut(&inode, block_index, self.fs.block_size, |block| {
|
||||
block.fill(0);
|
||||
|
||||
// Place dirent
|
||||
let total_len = size_of::<Dirent>() + name.len();
|
||||
let aligned_len = (total_len + 3) & !3;
|
||||
let dirent = Dirent {
|
||||
ino,
|
||||
ent_size: aligned_len as _,
|
||||
type_indicator: 0, // TODO
|
||||
name_length_low: name.len() as u8,
|
||||
};
|
||||
block[..size_of::<Dirent>()].copy_from_slice(bytemuck::bytes_of(&dirent));
|
||||
block[size_of::<Dirent>()..total_len].copy_from_slice(name.as_bytes());
|
||||
// Place dirent
|
||||
let total_len = size_of::<Dirent>() + name.len();
|
||||
let aligned_len = (total_len + 3) & !3;
|
||||
let dirent = Dirent {
|
||||
ino,
|
||||
ent_size: aligned_len as _,
|
||||
type_indicator: 0, // TODO
|
||||
name_length_low: name.len() as u8,
|
||||
};
|
||||
block[..size_of::<Dirent>()]
|
||||
.copy_from_slice(bytemuck::bytes_of(&dirent));
|
||||
block[size_of::<Dirent>()..total_len].copy_from_slice(name.as_bytes());
|
||||
|
||||
// Fill the rest with empty blocks
|
||||
let dummy = Dirent {
|
||||
ino: 0,
|
||||
ent_size: (self.fs.block_size - aligned_len).try_into().unwrap(),
|
||||
type_indicator: 0,
|
||||
name_length_low: 0,
|
||||
};
|
||||
block[aligned_len..aligned_len + size_of::<Dirent>()]
|
||||
.copy_from_slice(bytemuck::bytes_of(&dummy));
|
||||
// Fill the rest with empty blocks
|
||||
let dummy = Dirent {
|
||||
ino: 0,
|
||||
ent_size: (self.fs.block_size - aligned_len).try_into().unwrap(),
|
||||
type_indicator: 0,
|
||||
name_length_low: 0,
|
||||
};
|
||||
block[aligned_len..aligned_len + size_of::<Dirent>()]
|
||||
.copy_from_slice(bytemuck::bytes_of(&dummy));
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
holder.put().await?;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn prepare_for_removal(&self) -> Result<(), Error> {
|
||||
// Check that the directory doesn't have any children besides . and .. and set the locked
|
||||
// flag to prevent anyone from adding entries while the directory is being unlinked
|
||||
// TODO find a better way to do this
|
||||
let mut holder = self.inode.get_mut().await?;
|
||||
{
|
||||
let inode = holder.write();
|
||||
let n = inode.blocks(&self.fs);
|
||||
self.inode
|
||||
.amap_mut(async |inode| {
|
||||
let n = inode.blocks(&self.fs);
|
||||
|
||||
for i in 0..n {
|
||||
self.fs
|
||||
.with_inode_block(&inode, i as _, |block| {
|
||||
let iter = DirentIter::new(&self.fs, block, 0);
|
||||
for i in 0..n {
|
||||
self.fs
|
||||
.with_inode_block(&inode, i as _, |block| {
|
||||
let iter = DirentIter::new(&self.fs, block, 0);
|
||||
|
||||
for (_, name, _) in iter {
|
||||
if name != b"." && name != b".." {
|
||||
return Err(Error::IsADirectory);
|
||||
for (_, name, _) in iter {
|
||||
if name != b"." && name != b".." {
|
||||
return Err(Error::IsADirectory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
|
||||
self.locked.store(true, Ordering::Release);
|
||||
}
|
||||
holder.put().await?;
|
||||
Ok(())
|
||||
self.locked.store(true, Ordering::Release);
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn remove_entry(&self, child: &Node, name: &str) -> Result<(), Error> {
|
||||
@ -435,12 +430,10 @@ impl DirectoryNode {
|
||||
_ => return Err(Error::InvalidOperation),
|
||||
};
|
||||
|
||||
let ino = {
|
||||
let mut holder = self.inode.get_mut().await?;
|
||||
let mut result = None;
|
||||
|
||||
{
|
||||
let inode = holder.write();
|
||||
let ino = self
|
||||
.inode
|
||||
.amap_mut(async |inode| {
|
||||
let mut result = None;
|
||||
let n = inode.blocks(&self.fs);
|
||||
|
||||
for i in 0..n {
|
||||
@ -457,26 +450,24 @@ impl DirectoryNode {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
holder.put().await?;
|
||||
|
||||
result
|
||||
};
|
||||
result.ok_or(Error::DoesNotExist)
|
||||
})
|
||||
.await?;
|
||||
|
||||
let ino = ino.ok_or(Error::DoesNotExist)?;
|
||||
assert_eq!(ino, child_ino);
|
||||
log::info!("ext2: unlinked ino #{ino}");
|
||||
|
||||
// Decrement the refcount on the inode
|
||||
let mut holder = self.inode.cache().get_mut(ino).await?;
|
||||
{
|
||||
// TODO put the inode into the "free list" to be freed on cache flush/when no one is
|
||||
// holding a ref to it any longer
|
||||
let mut inode = holder.write();
|
||||
inode.hard_links = inode.hard_links.saturating_sub(1);
|
||||
inode.dtime = real_time().seconds as _;
|
||||
}
|
||||
holder.put().await?;
|
||||
let child = InodeAccess::new(self.inode.cache().clone(), ino);
|
||||
// TODO put the inode into the "free list" to be freed on cache flush/when no one is
|
||||
// holding a ref to it any longer
|
||||
child
|
||||
.map_mut(|inode| {
|
||||
inode.hard_links = inode.hard_links.saturating_sub(1);
|
||||
inode.dtime = real_time().seconds as u32;
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -484,36 +475,38 @@ impl DirectoryNode {
|
||||
async fn lookup_entry(&self, search_name: &str) -> Result<NodeRef, Error> {
|
||||
assert!(search_name.len() < 255);
|
||||
|
||||
let inode = self.inode.get().await?;
|
||||
let inode = inode.read();
|
||||
let n = inode.blocks(&self.fs) as u32;
|
||||
self.inode
|
||||
.amap(async |inode| {
|
||||
let n = inode.blocks(&self.fs) as u32;
|
||||
|
||||
for i in 0..n {
|
||||
let ino = self
|
||||
.fs
|
||||
.with_inode_block(&inode, i, |block| {
|
||||
let iter = DirentIter::new(&self.fs, block, 0);
|
||||
for i in 0..n {
|
||||
let ino = self
|
||||
.fs
|
||||
.with_inode_block(&inode, i, |block| {
|
||||
let iter = DirentIter::new(&self.fs, block, 0);
|
||||
|
||||
for (dirent, name, _) in iter {
|
||||
let Ok(name) = core::str::from_utf8(name) else {
|
||||
continue;
|
||||
};
|
||||
for (dirent, name, _) in iter {
|
||||
let Ok(name) = core::str::from_utf8(name) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if search_name == name {
|
||||
return Ok(Some(dirent.ino));
|
||||
}
|
||||
if search_name == name {
|
||||
return Ok(Some(dirent.ino));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
})
|
||||
.await?;
|
||||
|
||||
if let Some(ino) = ino {
|
||||
return self.fs.load_node(ino).await;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
})
|
||||
.await?;
|
||||
|
||||
if let Some(ino) = ino {
|
||||
return self.fs.load_node(ino).await;
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::DoesNotExist)
|
||||
Err(Error::DoesNotExist)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn read_entries(
|
||||
@ -521,63 +514,64 @@ impl DirectoryNode {
|
||||
mut pos: u64,
|
||||
entries: &mut [MaybeUninit<DirectoryEntry>],
|
||||
) -> Result<(usize, u64), Error> {
|
||||
let inode = self.inode.get().await?;
|
||||
let inode = inode.read();
|
||||
self.inode
|
||||
.amap(async |inode| {
|
||||
let size = inode.size(&self.fs);
|
||||
if pos >= inode.size(&self.fs) || entries.is_empty() {
|
||||
return Ok((0, pos));
|
||||
}
|
||||
|
||||
let size = inode.size(&self.fs);
|
||||
if pos >= inode.size(&self.fs) || entries.is_empty() {
|
||||
return Ok((0, pos));
|
||||
}
|
||||
|
||||
loop {
|
||||
if pos >= size {
|
||||
return Ok((0, pos));
|
||||
}
|
||||
|
||||
let index = pos / self.fs.block_size as u64;
|
||||
let offset = (pos % self.fs.block_size as u64) as usize;
|
||||
|
||||
let (entry_count, new_pos) = self
|
||||
.fs
|
||||
.with_inode_block(&inode, index as u32, |block| {
|
||||
let mut pos = pos;
|
||||
let mut entry_count = 0;
|
||||
|
||||
let iter = DirentIter::new(&self.fs, block, offset);
|
||||
|
||||
for (dirent, name, entry_offset) in iter {
|
||||
pos = (index * self.fs.block_size as u64) + entry_offset as u64;
|
||||
|
||||
if entry_count >= entries.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
pos += dirent.ent_size as u64;
|
||||
|
||||
if let Ok(name) = core::str::from_utf8(name) {
|
||||
entries[entry_count].write(DirectoryEntry {
|
||||
ty: None,
|
||||
name: FixedString::from_str(name)?,
|
||||
});
|
||||
|
||||
entry_count += 1;
|
||||
}
|
||||
loop {
|
||||
if pos >= size {
|
||||
return Ok((0, pos));
|
||||
}
|
||||
|
||||
Ok((entry_count, pos))
|
||||
})
|
||||
.await?;
|
||||
let index = pos / self.fs.block_size as u64;
|
||||
let offset = (pos % self.fs.block_size as u64) as usize;
|
||||
|
||||
pos = new_pos;
|
||||
let (entry_count, new_pos) = self
|
||||
.fs
|
||||
.with_inode_block(&inode, index as u32, |block| {
|
||||
let mut pos = pos;
|
||||
let mut entry_count = 0;
|
||||
|
||||
// If read any entries from the block, return
|
||||
if entry_count != 0 {
|
||||
return Ok((entry_count, pos));
|
||||
}
|
||||
let iter = DirentIter::new(&self.fs, block, offset);
|
||||
|
||||
// Otherwise go to next block
|
||||
pos = (index + 1) * self.fs.block_size as u64;
|
||||
}
|
||||
for (dirent, name, entry_offset) in iter {
|
||||
pos = (index * self.fs.block_size as u64) + entry_offset as u64;
|
||||
|
||||
if entry_count >= entries.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
pos += dirent.ent_size as u64;
|
||||
|
||||
if let Ok(name) = core::str::from_utf8(name) {
|
||||
entries[entry_count].write(DirectoryEntry {
|
||||
ty: None,
|
||||
name: FixedString::from_str(name)?,
|
||||
});
|
||||
|
||||
entry_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok((entry_count, pos))
|
||||
})
|
||||
.await?;
|
||||
|
||||
pos = new_pos;
|
||||
|
||||
// If read any entries from the block, return
|
||||
if entry_count != 0 {
|
||||
return Ok((entry_count, pos));
|
||||
}
|
||||
|
||||
// Otherwise go to next block
|
||||
pos = (index + 1) * self.fs.block_size as u64;
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@ -587,18 +581,14 @@ impl CommonImpl for DirectoryNode {
|
||||
}
|
||||
|
||||
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
|
||||
let inode = block!(self.inode.get().await)??;
|
||||
let inode = inode.read();
|
||||
Ok(inode.size(&self.fs))
|
||||
block!(self.inode.size().await)?
|
||||
}
|
||||
|
||||
fn metadata(&self, _node: &NodeRef) -> Result<Metadata, Error> {
|
||||
let inode = block!(self.inode.get().await)??;
|
||||
let inode = inode.read();
|
||||
Ok(inode.metadata(&self.fs, self.inode.ino()))
|
||||
block!(self.inode.metadata().await)?
|
||||
}
|
||||
|
||||
fn set_metadata(&self, node: &NodeRef, metadata: &Metadata) -> Result<(), Error> {
|
||||
fn set_metadata(&self, _node: &NodeRef, metadata: &Metadata) -> Result<(), Error> {
|
||||
block!(self.inode.update_metadata(metadata).await)?
|
||||
}
|
||||
}
|
||||
@ -620,7 +610,11 @@ impl DirectoryImpl for DirectoryNode {
|
||||
if self.fs.force_readonly {
|
||||
return Err(Error::ReadOnly);
|
||||
}
|
||||
block!(self.fs.create_node(Some(self.inode.ino()), ty).await)?
|
||||
let mode = match ty {
|
||||
FileType::Directory | FileType::Symlink => FileMode::default_dir(),
|
||||
_ => FileMode::default_file(),
|
||||
};
|
||||
block!(InodeAccess::allocate(&self.fs, ty, mode, Some(self.inode.ino())).await)?
|
||||
}
|
||||
|
||||
fn attach_node(&self, _parent: &NodeRef, child: &NodeRef, name: &str) -> Result<(), Error> {
|
||||
|
@ -29,61 +29,55 @@ impl RegularNode {
|
||||
}
|
||||
|
||||
async fn resize(&self, new_size: u64) -> Result<(), Error> {
|
||||
let mut holder = self.inode.get_mut().await?;
|
||||
let mut inode = holder.write();
|
||||
inode.resize(&self.fs, new_size).await?;
|
||||
drop(inode);
|
||||
holder.put().await?;
|
||||
Ok(())
|
||||
self.inode
|
||||
.amap_mut(async |inode| {
|
||||
inode.resize(&self.fs, new_size).await?;
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn read(&self, pos: u64, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
let holder = self.inode.get().await?;
|
||||
let inode = holder.read();
|
||||
|
||||
self.fs.read_inode_data(&inode, pos, buffer).await
|
||||
self.inode
|
||||
.amap(async |inode| self.fs.read_inode_data(inode, pos, buffer).await)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn write(&self, mut pos: u64, buffer: &[u8]) -> Result<usize, Error> {
|
||||
let mut holder = self.inode.get_mut().await?;
|
||||
let mut inode = holder.write();
|
||||
self.inode
|
||||
.amap_mut(async |inode| {
|
||||
let need_size = pos + buffer.len() as u64;
|
||||
inode.reserve(&self.fs, need_size).await?;
|
||||
|
||||
let need_size = pos + buffer.len() as u64;
|
||||
inode.reserve(&self.fs, need_size).await?;
|
||||
let mut offset = 0;
|
||||
let mut remaining = buffer.len();
|
||||
|
||||
let mut offset = 0;
|
||||
let mut remaining = buffer.len();
|
||||
while remaining != 0 {
|
||||
let block_index = pos / self.fs.block_size as u64;
|
||||
let block_offset = (pos % self.fs.block_size as u64) as usize;
|
||||
let amount = remaining.min(self.fs.block_size - block_offset);
|
||||
|
||||
while remaining != 0 {
|
||||
let block_index = pos / self.fs.block_size as u64;
|
||||
let block_offset = (pos % self.fs.block_size as u64) as usize;
|
||||
let amount = remaining.min(self.fs.block_size - block_offset);
|
||||
self.fs
|
||||
.with_inode_block_mut(&inode, block_index as u32, amount, |block| {
|
||||
block[block_offset..block_offset + amount]
|
||||
.copy_from_slice(&buffer[offset..offset + amount]);
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
self.fs
|
||||
.with_inode_block_mut(&inode, block_index as u32, amount, |block| {
|
||||
block[block_offset..block_offset + amount]
|
||||
.copy_from_slice(&buffer[offset..offset + amount]);
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
pos += amount as u64;
|
||||
offset += amount;
|
||||
remaining -= amount;
|
||||
}
|
||||
|
||||
drop(inode);
|
||||
holder.put().await?;
|
||||
|
||||
Ok(offset)
|
||||
pos += amount as u64;
|
||||
offset += amount;
|
||||
remaining -= amount;
|
||||
}
|
||||
Ok(offset)
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl CommonImpl for RegularNode {
|
||||
fn metadata(&self, _node: &NodeRef) -> Result<Metadata, Error> {
|
||||
let inode = block!(self.inode.get().await)??;
|
||||
let inode = inode.read();
|
||||
Ok(inode.metadata(&self.fs, self.inode.ino()))
|
||||
block!(self.inode.metadata().await)?
|
||||
}
|
||||
|
||||
fn set_metadata(&self, _node: &NodeRef, metadata: &Metadata) -> Result<(), Error> {
|
||||
@ -95,9 +89,7 @@ impl CommonImpl for RegularNode {
|
||||
}
|
||||
|
||||
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
|
||||
let inode = block!(self.inode.get().await)??;
|
||||
let inode = inode.read();
|
||||
Ok(inode.size(&self.fs))
|
||||
block!(self.inode.size().await)?
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,18 +1,27 @@
|
||||
use core::{
|
||||
cmp::Ordering,
|
||||
ops::{Deref, DerefMut},
|
||||
ops::{AsyncFnOnce, Deref, DerefMut},
|
||||
};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use libk::{block, error::Error, task::sync::AsyncMutex, vfs::Metadata};
|
||||
use bytemuck::Zeroable;
|
||||
use libk::{
|
||||
block,
|
||||
error::Error,
|
||||
task::sync::AsyncMutex,
|
||||
time::real_time,
|
||||
vfs::{Metadata, NodeRef},
|
||||
};
|
||||
use libk_util::{
|
||||
lru_hash_table::LruCache,
|
||||
sync::spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockReadGuard, IrqSafeRwLockWriteGuard},
|
||||
};
|
||||
use yggdrasil_abi::io::FileType;
|
||||
use yggdrasil_abi::io::{FileMode, FileType};
|
||||
|
||||
use crate::{
|
||||
data::{FsReadonlyFeatures, InodeMode, DIRECT_BLOCK_COUNT},
|
||||
dir::DirectoryNode,
|
||||
file::RegularNode,
|
||||
Ext2Fs, Inode,
|
||||
};
|
||||
|
||||
@ -73,12 +82,63 @@ impl InodeAccess {
|
||||
&self.inode_cache
|
||||
}
|
||||
|
||||
pub async fn get(&self) -> Result<InodeRef, Error> {
|
||||
self.inode_cache.get(self.ino).await
|
||||
pub async fn map<T, F: FnOnce(&Inode) -> Result<T, Error>>(
|
||||
&self,
|
||||
mapper: F,
|
||||
) -> Result<T, Error> {
|
||||
let inode = self.inode_cache.get(self.ino).await?;
|
||||
let result = {
|
||||
let lock = inode.read();
|
||||
mapper(&*lock)
|
||||
};
|
||||
result
|
||||
}
|
||||
|
||||
pub async fn get_mut(&self) -> Result<InodeMut, Error> {
|
||||
self.inode_cache.get_mut(self.ino).await
|
||||
pub async fn map_mut<T, F: FnOnce(&mut Inode) -> Result<T, Error>>(
|
||||
&self,
|
||||
mapper: F,
|
||||
) -> Result<T, Error> {
|
||||
let mut inode = self.inode_cache.get_mut(self.ino).await?;
|
||||
let result = {
|
||||
let mut lock = inode.write();
|
||||
mapper(&mut *lock)
|
||||
};
|
||||
inode.put().await?;
|
||||
result
|
||||
}
|
||||
|
||||
pub async fn amap<T, F: AsyncFnOnce(&Inode) -> Result<T, Error>>(
|
||||
&self,
|
||||
mapper: F,
|
||||
) -> Result<T, Error> {
|
||||
let inode = self.inode_cache.get(self.ino).await?;
|
||||
let result = {
|
||||
let lock = inode.read();
|
||||
mapper(&*lock).await
|
||||
};
|
||||
result
|
||||
}
|
||||
|
||||
pub async fn amap_mut<T, F: AsyncFnOnce(&mut Inode) -> Result<T, Error>>(
|
||||
&self,
|
||||
mapper: F,
|
||||
) -> Result<T, Error> {
|
||||
let mut inode = self.inode_cache.get_mut(self.ino).await?;
|
||||
let result = {
|
||||
let mut lock = inode.write();
|
||||
mapper(&mut *lock).await
|
||||
};
|
||||
inode.put().await?;
|
||||
result
|
||||
}
|
||||
|
||||
pub async fn metadata(&self) -> Result<Metadata, Error> {
|
||||
self.map(|inode| Ok(inode.metadata(&self.inode_cache.fs, self.ino)))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn size(&self) -> Result<u64, Error> {
|
||||
self.map(|inode| Ok(inode.size(&self.inode_cache.fs))).await
|
||||
}
|
||||
|
||||
pub async fn update_metadata(&self, metadata: &Metadata) -> Result<(), Error> {
|
||||
@ -93,10 +153,7 @@ impl InodeAccess {
|
||||
.try_into()
|
||||
.map_err(|_| Error::InvalidArgument)?;
|
||||
|
||||
let mut holder = self.get_mut().await?;
|
||||
{
|
||||
let mut inode = holder.write();
|
||||
|
||||
self.map_mut(|inode| {
|
||||
inode.mtime = metadata.mtime as _;
|
||||
inode.atime = metadata.mtime as _;
|
||||
inode.ctime = metadata.ctime as _;
|
||||
@ -104,8 +161,75 @@ impl InodeAccess {
|
||||
inode.mode.update_permissions(metadata.mode);
|
||||
inode.uid = uid;
|
||||
inode.gid = gid;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn populate_inode(
|
||||
fs: &Arc<Ext2Fs>,
|
||||
ty: FileType,
|
||||
mode: FileMode,
|
||||
parent_ino: Option<u32>,
|
||||
ino: u32,
|
||||
) -> Result<NodeRef, Error> {
|
||||
log::info!("ext2: allocated inode #{ino}");
|
||||
|
||||
let now = real_time().seconds as u32;
|
||||
|
||||
let mut imode = InodeMode::default_for_type(ty);
|
||||
imode.update_permissions(mode);
|
||||
|
||||
fs.write_inode(
|
||||
ino,
|
||||
&Inode {
|
||||
ctime: now,
|
||||
mtime: now,
|
||||
atime: now,
|
||||
mode: imode,
|
||||
..Inode::zeroed()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
let this = InodeAccess::new(fs.inode_cache.get().clone(), ino);
|
||||
let fs = fs.clone();
|
||||
let node = match ty {
|
||||
FileType::Directory => DirectoryNode::create(fs, this, parent_ino).await?,
|
||||
FileType::File => RegularNode::new(fs, this),
|
||||
FileType::Symlink => todo!(),
|
||||
_ => return Err(Error::NotImplemented),
|
||||
};
|
||||
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
pub async fn allocate(
|
||||
fs: &Arc<Ext2Fs>,
|
||||
ty: FileType,
|
||||
mode: FileMode,
|
||||
parent_ino: Option<u32>,
|
||||
) -> Result<NodeRef, Error> {
|
||||
if parent_ino.is_none() && ty != FileType::Directory {
|
||||
log::warn!("ext2: cannot allocate non-directory inode without a parent");
|
||||
return Err(Error::InvalidOperation);
|
||||
}
|
||||
if !matches!(ty, FileType::Symlink | FileType::Directory | FileType::File) {
|
||||
log::warn!("ext2: cannot allocate inode of type {ty:?}");
|
||||
return Err(Error::InvalidOperation);
|
||||
}
|
||||
|
||||
let ino = fs.allocate_inode(ty == FileType::Directory).await?;
|
||||
|
||||
match Self::populate_inode(fs, ty, mode, parent_ino, ino).await {
|
||||
Ok(node) => Ok(node),
|
||||
Err(error) => {
|
||||
log::warn!("ext2: couldn't set up inode #{ino}: {error:?}");
|
||||
// TODO free the inode and flush it from the cache
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
holder.put().await
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,7 +290,7 @@ impl InodeCache {
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
pub async fn get(&self, ino: u32) -> Result<InodeRef, Error> {
|
||||
async fn get(&self, ino: u32) -> Result<InodeRef, Error> {
|
||||
if self.cache.is_some() {
|
||||
let entry = self.entry(ino).await?;
|
||||
let inode = CachedInodeRef { entry };
|
||||
@ -184,7 +308,7 @@ impl InodeCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_mut(self: &Arc<Self>, ino: u32) -> Result<InodeMut, Error> {
|
||||
async fn get_mut(self: &Arc<Self>, ino: u32) -> Result<InodeMut, Error> {
|
||||
if self.cache.is_some() {
|
||||
let entry = self.entry(ino).await?;
|
||||
let inode = CachedInodeMut { entry };
|
||||
|
@ -7,22 +7,23 @@ extern crate alloc;
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use bytemuck::Zeroable;
|
||||
use data::{FsReadonlyFeatures, FsRequiredFeatures, InodeMode};
|
||||
use data::{FsReadonlyFeatures, FsRequiredFeatures};
|
||||
use dir::DirectoryNode;
|
||||
use file::RegularNode;
|
||||
use inode::{InodeAccess, InodeCache};
|
||||
use libk::{
|
||||
device::block::{cache::DeviceMapper, BlockDevice},
|
||||
error::Error,
|
||||
time::real_time,
|
||||
vfs::{Filesystem, FilesystemMountOption, NodeRef},
|
||||
};
|
||||
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
|
||||
|
||||
mod allocation;
|
||||
mod data;
|
||||
pub mod inode;
|
||||
|
||||
pub mod dir;
|
||||
pub mod file;
|
||||
pub mod inode;
|
||||
pub mod symlink;
|
||||
|
||||
pub use data::{BlockGroupDescriptor, Dirent, ExtendedSuperblock, Inode};
|
||||
@ -245,11 +246,21 @@ impl Ext2Fs {
|
||||
let block_group_block_count = superblock.block_group_block_count;
|
||||
|
||||
log::info!("Inode size: {}", superblock.inode_size());
|
||||
log::info!("Total free inodes: {}", superblock.total_unallocated_inodes);
|
||||
log::info!("Total free blocks: {}", superblock.total_unallocated_blocks);
|
||||
|
||||
let mapper = match cached {
|
||||
false => DeviceMapper::uncached(device, block_size),
|
||||
false => DeviceMapper::uncached(device, block_size, "ext2")?,
|
||||
true => {
|
||||
DeviceMapper::cached_with_capacity(device, block_size, block_size * 16, 128, 64)
|
||||
let segment_size = (block_size * 16).min(65536);
|
||||
DeviceMapper::cached_with_capacity(
|
||||
device,
|
||||
block_size,
|
||||
segment_size,
|
||||
128,
|
||||
64,
|
||||
"ext2",
|
||||
)?
|
||||
}
|
||||
};
|
||||
|
||||
@ -284,8 +295,8 @@ impl Ext2Fs {
|
||||
|
||||
pub async fn load_node(self: &Arc<Self>, ino: u32) -> Result<NodeRef, Error> {
|
||||
let cache = self.inode_cache.get();
|
||||
let mode = cache.get(ino).await?.read().mode;
|
||||
let inode = InodeAccess::new(cache.clone(), ino);
|
||||
let mode = inode.map(|inode| Ok(inode.mode)).await?;
|
||||
match mode.node_type() {
|
||||
Some(FileType::Directory) => Ok(DirectoryNode::new(self.clone(), inode)),
|
||||
Some(FileType::File) => Ok(RegularNode::new(self.clone(), inode)),
|
||||
@ -484,6 +495,11 @@ impl Ext2Fs {
|
||||
let state = self.state.read();
|
||||
if state.dirty {
|
||||
log::info!("Flushing superblock");
|
||||
log::info!(
|
||||
"inodes {} blocks {}",
|
||||
state.superblock.total_unallocated_inodes,
|
||||
state.superblock.total_unallocated_blocks
|
||||
);
|
||||
self.mapper
|
||||
.device()
|
||||
.write_exact(
|
||||
@ -495,156 +511,6 @@ impl Ext2Fs {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn allocate<
|
||||
F: Fn(&mut BlockGroupDescriptor) -> Option<(u32, u32)>,
|
||||
G: Fn(&mut ExtendedSuperblock),
|
||||
>(
|
||||
&self,
|
||||
descriptor_mapper: F,
|
||||
superblock_mapper: G,
|
||||
) -> Result<u32, Error> {
|
||||
let bit_per_block = self.block_size * 8;
|
||||
for group_index in 0..self.bgdt.entry_count as u32 {
|
||||
let bitmap = self
|
||||
.with_bgdt_entry_mut(group_index, |descriptor| {
|
||||
match descriptor_mapper(descriptor) {
|
||||
Some(val) => Ok(Some(val)),
|
||||
_ => Ok(None),
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
if let Some((bitmap, group_item_count)) = bitmap {
|
||||
let no = self
|
||||
.with_block_mut(bitmap, 0, |bitmap| {
|
||||
for i in 0..bit_per_block.min(group_item_count as usize) {
|
||||
let index = i / 8;
|
||||
let bit = 1u8 << (i % 8);
|
||||
|
||||
if bitmap[index] & bit == 0 {
|
||||
bitmap[index] |= bit;
|
||||
return Ok(Some(i as u32));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
})
|
||||
.await?
|
||||
.expect("TODO: bgdt says there're things, but bitmap says there aren't");
|
||||
|
||||
{
|
||||
let mut state = self.state.write();
|
||||
superblock_mapper(&mut state.superblock);
|
||||
state.dirty = true;
|
||||
}
|
||||
|
||||
if !self.cached {
|
||||
self.flush_superblock().await?;
|
||||
}
|
||||
|
||||
return Ok(group_index * group_item_count + no);
|
||||
}
|
||||
}
|
||||
|
||||
todo!("ENOSPC")
|
||||
}
|
||||
|
||||
async fn allocate_inode(&self, directory: bool) -> Result<u32, Error> {
|
||||
let ino = self
|
||||
.allocate(
|
||||
|descriptor| {
|
||||
if descriptor.unallocated_inodes == 0
|
||||
|| (directory && descriptor.directories == u16::MAX)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
if directory {
|
||||
descriptor.directories += 1;
|
||||
}
|
||||
descriptor.unallocated_inodes -= 1;
|
||||
Some((descriptor.inode_usage_bitmap, self.block_group_inode_count))
|
||||
}
|
||||
},
|
||||
|superblock| {
|
||||
superblock.total_unallocated_inodes -= 1;
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
let ino = ino + 1;
|
||||
|
||||
log::debug!("ext2: allocated inode #{ino}");
|
||||
|
||||
Ok(ino)
|
||||
}
|
||||
|
||||
async fn allocate_block(&self) -> Result<u32, Error> {
|
||||
let no = self
|
||||
.allocate(
|
||||
|descriptor| {
|
||||
if descriptor.unallocated_blocks == 0 {
|
||||
None
|
||||
} else {
|
||||
descriptor.unallocated_blocks -= 1;
|
||||
Some((descriptor.block_usage_bitmap, self.block_group_block_count))
|
||||
}
|
||||
},
|
||||
|superblock| {
|
||||
superblock.total_unallocated_blocks -= 1;
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
log::debug!("ext2: allocated block #{no}");
|
||||
|
||||
Ok(no)
|
||||
}
|
||||
|
||||
async fn create_node(
|
||||
self: &Arc<Self>,
|
||||
parent: Option<u32>,
|
||||
ty: FileType,
|
||||
) -> Result<NodeRef, Error> {
|
||||
let inode_cache = self.inode_cache.get();
|
||||
let ino = self.allocate_inode(ty == FileType::Directory).await?;
|
||||
|
||||
log::info!("Allocated inode #{ino}");
|
||||
|
||||
let access = InodeAccess::new(inode_cache.clone(), ino);
|
||||
let now = real_time().seconds as u32;
|
||||
|
||||
{
|
||||
// Write initial inode
|
||||
let mut inode = access.get_mut().await?;
|
||||
|
||||
{
|
||||
let mut data = inode.write();
|
||||
|
||||
// Create new inode struct
|
||||
let mut value = Inode::zeroed();
|
||||
value.mode = InodeMode::default_for_type(ty);
|
||||
value.ctime = now;
|
||||
value.mtime = now;
|
||||
value.atime = now;
|
||||
|
||||
**data = value;
|
||||
}
|
||||
|
||||
inode.put().await?;
|
||||
}
|
||||
|
||||
// TODO dealloc ino if failed
|
||||
// self.write_inode(ino, &inode).await.unwrap();
|
||||
|
||||
let node = match ty {
|
||||
FileType::Directory => DirectoryNode::create(self.clone(), access, parent)
|
||||
.await
|
||||
.unwrap(),
|
||||
FileType::File => RegularNode::new(self.clone(), access),
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
async fn read_index(&self, block_index: u32, index: usize) -> Result<u32, Error> {
|
||||
self.with_block(block_index, |block| {
|
||||
let indirect: &[u32] = unsafe {
|
||||
|
@ -31,38 +31,39 @@ impl SymlinkNode {
|
||||
}
|
||||
|
||||
async fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
let inode = self.inode.get().await?;
|
||||
let inode = inode.read();
|
||||
self.inode
|
||||
.amap(async |inode| {
|
||||
let len = inode.size(&self.fs) as usize;
|
||||
if len > self.fs.block_size {
|
||||
log::warn!("ext2: symlink size > block size");
|
||||
return Err(Error::InvalidFile);
|
||||
}
|
||||
if buf.len() < len {
|
||||
return Err(Error::BufferTooSmall);
|
||||
}
|
||||
|
||||
let len = inode.size(&self.fs) as usize;
|
||||
if len > self.fs.block_size {
|
||||
log::warn!("ext2: symlink size > block size");
|
||||
return Err(Error::InvalidFile);
|
||||
}
|
||||
if buf.len() < len {
|
||||
return Err(Error::BufferTooSmall);
|
||||
}
|
||||
let mut write = self.cache.write();
|
||||
write.clear();
|
||||
|
||||
let mut write = self.cache.write();
|
||||
write.clear();
|
||||
// If length of symlink is lower than 60, data is stored directly in "block address"
|
||||
// section of the inode
|
||||
if len < 60 {
|
||||
let bytes = unsafe { Self::link_from_inode_blocks(&inode, len) };
|
||||
write.extend_from_slice(bytes);
|
||||
buf[..len].copy_from_slice(bytes);
|
||||
} else {
|
||||
self.fs
|
||||
.with_inode_block(&inode, 0, |block| {
|
||||
write.extend_from_slice(&block[..len]);
|
||||
buf[..len].copy_from_slice(&block[..len]);
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
|
||||
// If length of symlink is lower than 60, data is stored directly in "block address"
|
||||
// section of the inode
|
||||
if len < 60 {
|
||||
let bytes = unsafe { Self::link_from_inode_blocks(&inode, len) };
|
||||
write.extend_from_slice(bytes);
|
||||
buf[..len].copy_from_slice(bytes);
|
||||
} else {
|
||||
self.fs
|
||||
.with_inode_block(&inode, 0, |block| {
|
||||
write.extend_from_slice(&block[..len]);
|
||||
buf[..len].copy_from_slice(&block[..len]);
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(len)
|
||||
Ok(len)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
unsafe fn link_from_inode_blocks(inode: &Inode, len: usize) -> &[u8] {
|
||||
@ -73,9 +74,7 @@ impl SymlinkNode {
|
||||
|
||||
impl CommonImpl for SymlinkNode {
|
||||
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
|
||||
let inode = block!(self.inode.get().await)??;
|
||||
let inode = inode.read();
|
||||
Ok(inode.size(&self.fs))
|
||||
block!(self.inode.size().await)?
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
@ -83,9 +82,7 @@ impl CommonImpl for SymlinkNode {
|
||||
}
|
||||
|
||||
fn metadata(&self, _node: &NodeRef) -> Result<Metadata, Error> {
|
||||
let inode = block!(self.inode.get().await)??;
|
||||
let inode = inode.read();
|
||||
Ok(inode.metadata(&self.fs, self.inode.ino()))
|
||||
block!(self.inode.metadata().await)?
|
||||
}
|
||||
|
||||
fn set_metadata(&self, _node: &NodeRef, metadata: &Metadata) -> Result<(), Error> {
|
||||
|
@ -52,18 +52,24 @@ impl DeviceMapper {
|
||||
segment_size: usize,
|
||||
bucket_capacity: usize,
|
||||
bucket_count: usize,
|
||||
) -> DeviceMapper {
|
||||
filesystem: &str,
|
||||
) -> Result<DeviceMapper, Error> {
|
||||
DeviceMapper::cached_with_capacity_in(
|
||||
device,
|
||||
block_size,
|
||||
segment_size,
|
||||
bucket_capacity,
|
||||
bucket_count,
|
||||
filesystem,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn uncached(device: Arc<dyn BlockDevice>, block_size: usize) -> DeviceMapper {
|
||||
DeviceMapper::uncached_in(device, block_size)
|
||||
pub fn uncached(
|
||||
device: Arc<dyn BlockDevice>,
|
||||
block_size: usize,
|
||||
filesystem: &str,
|
||||
) -> Result<DeviceMapper, Error> {
|
||||
DeviceMapper::uncached_in(device, block_size, filesystem)
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,27 +80,37 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> DeviceMapper<A> {
|
||||
segment_size: usize,
|
||||
bucket_capacity: usize,
|
||||
bucket_count: usize,
|
||||
) -> DeviceMapper<A> {
|
||||
let cache = BlockCache::<A>::with_capacity_in(
|
||||
filesystem: &str,
|
||||
) -> Result<DeviceMapper<A>, Error> {
|
||||
BlockCache::<A>::with_capacity_in(
|
||||
device,
|
||||
block_size,
|
||||
segment_size,
|
||||
bucket_capacity,
|
||||
bucket_count,
|
||||
);
|
||||
DeviceMapper::<A>::Cached(cache)
|
||||
filesystem,
|
||||
)
|
||||
.map(DeviceMapper::<A>::Cached)
|
||||
}
|
||||
|
||||
pub fn uncached_in(device: Arc<dyn BlockDevice>, block_size: usize) -> DeviceMapper<A> {
|
||||
pub fn uncached_in(
|
||||
device: Arc<dyn BlockDevice>,
|
||||
block_size: usize,
|
||||
filesystem: &str,
|
||||
) -> Result<DeviceMapper<A>, Error> {
|
||||
if block_size % device.block_size() != 0 {
|
||||
panic!("Cache block size is not multiple of device block size");
|
||||
log::error!(
|
||||
"Couldn't create block mapper for {filesystem}: \
|
||||
cache block size is not a multiple of device block size"
|
||||
);
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
let uncache = UncachedCache::<A> {
|
||||
device,
|
||||
block_size,
|
||||
_pd: PhantomData,
|
||||
};
|
||||
Self::Uncached(uncache)
|
||||
Ok(Self::Uncached(uncache))
|
||||
}
|
||||
|
||||
pub fn device(&self) -> &Arc<dyn BlockDevice> {
|
||||
@ -179,21 +195,29 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> BlockCache<A> {
|
||||
segment_size: usize,
|
||||
bucket_capacity: usize,
|
||||
bucket_count: usize,
|
||||
) -> BlockCache<A> {
|
||||
assert_eq!(
|
||||
block_size % device.block_size(),
|
||||
0,
|
||||
"Block size is not a multiple of device block size"
|
||||
);
|
||||
assert_eq!(
|
||||
segment_size % block_size,
|
||||
0,
|
||||
"Segment size is not a multiple of block size"
|
||||
);
|
||||
assert!(
|
||||
segment_size >= block_size,
|
||||
"Segment size is less than block size"
|
||||
);
|
||||
filesystem: &str,
|
||||
) -> Result<BlockCache<A>, Error> {
|
||||
if block_size % device.block_size() != 0 {
|
||||
log::error!(
|
||||
"Couldn't create block cache for {filesystem}: \
|
||||
cache block size is not a multiple of device block size"
|
||||
);
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
if segment_size % block_size != 0 {
|
||||
log::error!(
|
||||
"Couldn't create block cache for {filesystem}: \
|
||||
segment size is not a multiple of block size"
|
||||
);
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
if segment_size < block_size {
|
||||
log::error!(
|
||||
"Couldn't create block cache for {filesystem}: \
|
||||
segment size is smaller than block size"
|
||||
);
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
log::info!("New block cache: block: {block_size}B, segment: {segment_size}B, geometry: {bucket_capacity}x{bucket_count}");
|
||||
log::info!(
|
||||
@ -201,12 +225,12 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> BlockCache<A> {
|
||||
(segment_size * bucket_capacity * bucket_count) / 1024
|
||||
);
|
||||
|
||||
BlockCache {
|
||||
Ok(BlockCache {
|
||||
device,
|
||||
block_size,
|
||||
segment_size,
|
||||
cache: AsyncMutex::new(LruCache::with_capacity(bucket_capacity, bucket_count)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn device(&self) -> &Arc<dyn BlockDevice> {
|
||||
|
@ -44,11 +44,15 @@ pub trait RegularImpl: CommonImpl {
|
||||
node: &NodeRef,
|
||||
opts: OpenOptions,
|
||||
) -> Result<(u64, Option<InstanceData>), Error> {
|
||||
let _ = node;
|
||||
let _ = opts;
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
/// Closes a file
|
||||
fn close(&self, node: &NodeRef, instance: Option<&InstanceData>) -> Result<(), Error> {
|
||||
let _ = node;
|
||||
let _ = instance;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -60,6 +64,10 @@ pub trait RegularImpl: CommonImpl {
|
||||
pos: u64,
|
||||
buf: &mut [u8],
|
||||
) -> Result<usize, Error> {
|
||||
let _ = node;
|
||||
let _ = instance;
|
||||
let _ = pos;
|
||||
let _ = buf;
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
@ -71,11 +79,17 @@ pub trait RegularImpl: CommonImpl {
|
||||
pos: u64,
|
||||
buf: &[u8],
|
||||
) -> Result<usize, Error> {
|
||||
let _ = node;
|
||||
let _ = instance;
|
||||
let _ = pos;
|
||||
let _ = buf;
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
/// Resizes the file to requested size
|
||||
fn truncate(&self, node: &NodeRef, new_size: u64) -> Result<(), Error> {
|
||||
let _ = node;
|
||||
let _ = new_size;
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
}
|
||||
@ -85,6 +99,7 @@ pub trait DirectoryImpl: CommonImpl {
|
||||
/// Opens a directory for reading its entries. Returns [DirectoryOpenPosition] to specify the
|
||||
/// starting position.
|
||||
fn open(&self, node: &NodeRef) -> Result<DirectoryOpenPosition, Error> {
|
||||
let _ = node;
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
@ -95,31 +110,45 @@ pub trait DirectoryImpl: CommonImpl {
|
||||
pos: u64,
|
||||
entries: &mut [MaybeUninit<DirectoryEntry>],
|
||||
) -> Result<(usize, u64), Error> {
|
||||
let _ = node;
|
||||
let _ = pos;
|
||||
let _ = entries;
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
/// Creates a child node, but does not associate it with the directory yet
|
||||
fn create_node(&self, parent: &NodeRef, ty: FileType) -> Result<NodeRef, Error> {
|
||||
let _ = parent;
|
||||
let _ = ty;
|
||||
Err(Error::ReadOnly)
|
||||
}
|
||||
|
||||
/// Associates the given node with the directory, creating an entry for it inside
|
||||
fn attach_node(&self, parent: &NodeRef, child: &NodeRef, name: &str) -> Result<(), Error> {
|
||||
let _ = parent;
|
||||
let _ = child;
|
||||
let _ = name;
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
/// Removes an entry of the directory with given name
|
||||
fn unlink_node(&self, parent: &NodeRef, child: &NodeRef, name: &str) -> Result<(), Error> {
|
||||
let _ = parent;
|
||||
let _ = child;
|
||||
let _ = name;
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
/// Fetches the child of the directory with given name
|
||||
fn lookup(&self, node: &NodeRef, name: &str) -> Result<NodeRef, Error> {
|
||||
let _ = node;
|
||||
let _ = name;
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
/// Returns the "length" of the directory in entries
|
||||
fn len(&self, node: &NodeRef) -> Result<usize, Error> {
|
||||
let _ = node;
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
}
|
||||
@ -144,6 +173,7 @@ pub trait SymlinkImpl: CommonImpl {
|
||||
|
||||
/// Fetches the contents of the symlink into a buffer
|
||||
fn read_link(&self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
let _ = buf;
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user