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 alloc::sync::Arc;
|
||||||
use libk::{
|
use libk::{
|
||||||
block,
|
block,
|
||||||
error::Error,
|
error::Error,
|
||||||
|
time::real_time,
|
||||||
vfs::{CommonImpl, DirectoryImpl, DirectoryOpenPosition, Metadata, Node, NodeFlags, NodeRef},
|
vfs::{CommonImpl, DirectoryImpl, DirectoryOpenPosition, Metadata, Node, NodeFlags, NodeRef},
|
||||||
};
|
};
|
||||||
use yggdrasil_abi::{
|
use yggdrasil_abi::{
|
||||||
@ -30,11 +36,117 @@ struct DirentIterMut<'a> {
|
|||||||
offset: usize,
|
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> {
|
impl<'a> DirentIterMut<'a> {
|
||||||
pub fn new(fs: &'a Ext2Fs, block: &'a mut [u8], offset: usize) -> Self {
|
pub fn new(fs: &'a Ext2Fs, block: &'a mut [u8], offset: usize) -> Self {
|
||||||
Self { fs, block, offset }
|
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 {
|
pub fn try_fit(&mut self, name: &str, ino: u32) -> bool {
|
||||||
let _ = self.fs;
|
let _ = self.fs;
|
||||||
|
|
||||||
@ -185,6 +297,7 @@ impl DirectoryNode {
|
|||||||
let mut child = holder.write();
|
let mut child = holder.write();
|
||||||
|
|
||||||
child.inc_hard_count();
|
child.inc_hard_count();
|
||||||
|
child.dtime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.put().await?;
|
holder.put().await?;
|
||||||
@ -263,6 +376,53 @@ impl DirectoryNode {
|
|||||||
Ok(())
|
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> {
|
async fn lookup_entry(&self, search_name: &str) -> Result<NodeRef, Error> {
|
||||||
assert!(search_name.len() < 255);
|
assert!(search_name.len() < 255);
|
||||||
|
|
||||||
@ -416,12 +576,11 @@ impl DirectoryImpl for DirectoryNode {
|
|||||||
Ok(())
|
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 {
|
if self.fs.force_readonly {
|
||||||
return Err(Error::ReadOnly);
|
return Err(Error::ReadOnly);
|
||||||
}
|
}
|
||||||
log::error!("ext2: unlink_node not implemented");
|
block!(self.remove_entry(name).await)?
|
||||||
Err(Error::NotImplemented)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_entries(
|
fn read_entries(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user