ext2: add a simple unlink()
This commit is contained in:
parent
8c96a009ad
commit
ce8600a782
@ -1,9 +1,15 @@
|
||||
use core::{any::Any, mem::MaybeUninit, str::FromStr};
|
||||
// TODO: dedupe the code for iterating the directory records
|
||||
use core::{
|
||||
any::Any,
|
||||
mem::{self, MaybeUninit},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use libk::{
|
||||
block,
|
||||
error::Error,
|
||||
time::real_time,
|
||||
vfs::{CommonImpl, DirectoryImpl, DirectoryOpenPosition, Metadata, Node, NodeFlags, NodeRef},
|
||||
};
|
||||
use yggdrasil_abi::{
|
||||
@ -30,11 +36,117 @@ struct DirentIterMut<'a> {
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
struct Record<'a> {
|
||||
fs: &'a Ext2Fs,
|
||||
offset: usize,
|
||||
data: &'a mut [u8],
|
||||
}
|
||||
|
||||
impl Record<'_> {
|
||||
pub fn dirent(&self) -> &Dirent {
|
||||
bytemuck::from_bytes(&self.data[..size_of::<Dirent>()])
|
||||
}
|
||||
|
||||
pub fn dirent_mut(&mut self) -> &mut Dirent {
|
||||
bytemuck::from_bytes_mut(&mut self.data[..size_of::<Dirent>()])
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.dirent().ino == 0
|
||||
}
|
||||
|
||||
pub fn ino(&self) -> u32 {
|
||||
self.dirent().ino
|
||||
}
|
||||
|
||||
fn name_len(&self) -> usize {
|
||||
let len = self.dirent().name_length_low as usize;
|
||||
if !self
|
||||
.fs
|
||||
.required_features
|
||||
.contains(FsRequiredFeatures::DIRENT_TYPE_FIELD)
|
||||
{
|
||||
todo!();
|
||||
}
|
||||
len
|
||||
}
|
||||
|
||||
fn name_bytes(&self) -> &[u8] {
|
||||
if !self.is_empty() {
|
||||
&self.data[size_of::<Dirent>()..size_of::<Dirent>() + self.name_len()]
|
||||
} else {
|
||||
b""
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
self.name_len() + 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())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DirentIterMut<'a> {
|
||||
pub fn new(fs: &'a Ext2Fs, block: &'a mut [u8], offset: usize) -> Self {
|
||||
Self { fs, block, offset }
|
||||
}
|
||||
|
||||
fn next_record(&mut self) -> Option<Record> {
|
||||
if self.offset + size_of::<Dirent>() > self.block.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let dirent_struct_end = self.offset + size_of::<Dirent>();
|
||||
let dirent: &Dirent = bytemuck::from_bytes(&self.block[self.offset..dirent_struct_end]);
|
||||
let record_size = (dirent.ent_size as usize).max(size_of::<Dirent>());
|
||||
let record = Record {
|
||||
data: &mut self.block[self.offset..self.offset + record_size],
|
||||
fs: self.fs,
|
||||
offset: self.offset,
|
||||
};
|
||||
|
||||
self.offset += record_size;
|
||||
Some(record)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, search: &str) -> Option<u32> {
|
||||
if search.is_empty() || search == "." || search == ".." {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut ino = None;
|
||||
while let Some(mut record) = self.next_record() {
|
||||
if record.name_bytes() != search.as_bytes() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let dirent = record.dirent_mut();
|
||||
ino = Some(mem::replace(&mut dirent.ino, 0));
|
||||
break;
|
||||
}
|
||||
|
||||
let ino = ino?;
|
||||
|
||||
// TODO compact the block
|
||||
|
||||
Some(ino)
|
||||
}
|
||||
|
||||
pub fn try_fit(&mut self, name: &str, ino: u32) -> bool {
|
||||
let _ = self.fs;
|
||||
|
||||
@ -185,6 +297,7 @@ impl DirectoryNode {
|
||||
let mut child = holder.write();
|
||||
|
||||
child.inc_hard_count();
|
||||
child.dtime = 0;
|
||||
}
|
||||
|
||||
holder.put().await?;
|
||||
@ -263,6 +376,53 @@ impl DirectoryNode {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_entry(&self, name: &str) -> Result<(), Error> {
|
||||
// TODO if unlinking a directory, check that it only contains . and ..
|
||||
assert!(name.len() < 255);
|
||||
|
||||
let ino = {
|
||||
let mut holder = self.inode.get_mut().await?;
|
||||
let mut result = None;
|
||||
|
||||
{
|
||||
let inode = holder.write();
|
||||
let n = inode.blocks(&self.fs);
|
||||
|
||||
for i in 0..n {
|
||||
let ino = self
|
||||
.fs
|
||||
.with_inode_block_mut(&inode, i as _, 0, |block| {
|
||||
let mut iter = DirentIterMut::new(&self.fs, block, 0);
|
||||
Ok(iter.remove(name))
|
||||
})
|
||||
.await?;
|
||||
|
||||
if let Some(ino) = ino {
|
||||
result = Some(ino);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
holder.put().await?;
|
||||
|
||||
result
|
||||
};
|
||||
|
||||
let ino = ino.ok_or(Error::DoesNotExist)?;
|
||||
log::info!("ext2: unlinked ino #{ino}");
|
||||
|
||||
// Decrement the refcount on the inode
|
||||
let mut holder = self.inode.cache().get_mut(ino).await?;
|
||||
{
|
||||
let mut inode = holder.write();
|
||||
inode.hard_links = inode.hard_links.saturating_sub(1);
|
||||
inode.dtime = real_time().seconds as _;
|
||||
}
|
||||
holder.put().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn lookup_entry(&self, search_name: &str) -> Result<NodeRef, Error> {
|
||||
assert!(search_name.len() < 255);
|
||||
|
||||
@ -416,12 +576,11 @@ impl DirectoryImpl for DirectoryNode {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unlink_node(&self, _parent: &NodeRef, _name: &str) -> Result<(), Error> {
|
||||
fn unlink_node(&self, _parent: &NodeRef, name: &str) -> Result<(), Error> {
|
||||
if self.fs.force_readonly {
|
||||
return Err(Error::ReadOnly);
|
||||
}
|
||||
log::error!("ext2: unlink_node not implemented");
|
||||
Err(Error::NotImplemented)
|
||||
block!(self.remove_entry(name).await)?
|
||||
}
|
||||
|
||||
fn read_entries(
|
||||
|
Loading…
x
Reference in New Issue
Block a user