ext2: metadata update

This commit is contained in:
Mark Poliakov 2024-12-29 19:43:08 +02:00
parent ce8600a782
commit 69c672bfca
11 changed files with 148 additions and 17 deletions

View File

@ -88,5 +88,14 @@ unexpected_cfgs = { level = "allow", check-cfg = ['cfg(rust_analyzer)'] }
[workspace.lints.clippy]
derivable_impls = { level = "allow" }
[profile.dev]
opt-level = 1
split-debuginfo = "packed"
lto = "thin"
panic = "abort"
[profile.dev.package."*"]
opt-level = 3
# [profile.dev]
# opt-level = "s"

View File

@ -274,6 +274,11 @@ impl InodeMode {
}
}
pub fn update_permissions(&mut self, mode: FileMode) {
self.0 &= !0o777;
self.0 |= (mode.bits() & 0o777) as u16;
}
pub fn permissions(&self) -> FileMode {
unsafe { FileMode::from_raw(self.0 as u32 & 0o777) }
}

View File

@ -414,6 +414,8 @@ impl DirectoryNode {
// 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 _;
@ -539,6 +541,10 @@ impl CommonImpl for DirectoryNode {
let inode = inode.read();
Ok(inode.metadata(&self.fs, self.inode.ino()))
}
fn set_metadata(&self, node: &NodeRef, metadata: &Metadata) -> Result<(), Error> {
block!(self.inode.update_metadata(metadata).await)?
}
}
impl DirectoryImpl for DirectoryNode {

View File

@ -86,6 +86,10 @@ impl CommonImpl for RegularNode {
Ok(inode.metadata(&self.fs, self.inode.ino()))
}
fn set_metadata(&self, _node: &NodeRef, metadata: &Metadata) -> Result<(), Error> {
block!(self.inode.update_metadata(metadata).await)?
}
fn as_any(&self) -> &dyn Any {
self
}

View File

@ -4,14 +4,15 @@ use core::{
};
use alloc::sync::Arc;
use libk::{block, error::Error, task::sync::AsyncMutex};
use libk::{block, error::Error, task::sync::AsyncMutex, vfs::Metadata};
use libk_util::{
lru_hash_table::LruCache,
sync::spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockReadGuard, IrqSafeRwLockWriteGuard},
};
use yggdrasil_abi::io::FileType;
use crate::{
data::{FsReadonlyFeatures, DIRECT_BLOCK_COUNT},
data::{FsReadonlyFeatures, InodeMode, DIRECT_BLOCK_COUNT},
Ext2Fs, Inode,
};
@ -79,6 +80,33 @@ impl InodeAccess {
pub async fn get_mut(&self) -> Result<InodeMut, Error> {
self.inode_cache.get_mut(self.ino).await
}
pub async fn update_metadata(&self, metadata: &Metadata) -> Result<(), Error> {
let uid = metadata
.uid
.bits()
.try_into()
.map_err(|_| Error::InvalidArgument)?;
let gid = metadata
.gid
.bits()
.try_into()
.map_err(|_| Error::InvalidArgument)?;
let mut holder = self.get_mut().await?;
{
let mut inode = holder.write();
inode.mtime = metadata.mtime as _;
inode.atime = metadata.mtime as _;
inode.ctime = metadata.ctime as _;
inode.mode.update_permissions(metadata.mode);
inode.uid = uid;
inode.gid = gid;
}
holder.put().await
}
}
impl InodeCache {

View File

@ -14,6 +14,7 @@ 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};
@ -608,6 +609,7 @@ impl Ext2Fs {
log::info!("Allocated inode #{ino}");
let access = InodeAccess::new(inode_cache.clone(), ino);
let now = real_time().seconds as u32;
{
// Write initial inode
@ -619,6 +621,9 @@ impl Ext2Fs {
// 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;
}

View File

@ -87,6 +87,10 @@ impl CommonImpl for SymlinkNode {
let inode = inode.read();
Ok(inode.metadata(&self.fs, self.inode.ino()))
}
fn set_metadata(&self, _node: &NodeRef, metadata: &Metadata) -> Result<(), Error> {
block!(self.inode.update_metadata(metadata).await)?
}
}
impl SymlinkImpl for SymlinkNode {

View File

@ -169,8 +169,7 @@ impl Node {
// TODO sanity checks: source and destination is not the same
// filenames are valid
if !Self::is_same_filesystem(source, destination) {
// TODO an error for this
return Err(Error::InvalidOperation);
return Err(Error::CrossDeviceLink);
}
let source_dir = source.as_directory()?;
@ -218,10 +217,11 @@ impl Node {
}
let mut cache = self.props.lock();
let common = self.data_as_common();
let metadata = cache
.metadata
.get_or_try_insert_with(|| self.data_as_common().metadata(self))?;
.get_or_try_insert_with(|| common.metadata(self))?;
// let mut metadata = self.metadata()?;
@ -237,8 +237,7 @@ impl Node {
if !self.flags.contains(NodeFlags::IN_MEMORY_PROPS) {
// Update permissions in the real node
// todo!();
log::error!("TODO: update real node metadata");
common.set_metadata(self, &metadata)?;
}
Ok(())
@ -246,12 +245,13 @@ impl Node {
/// Returns the "metadata" of the file: uid, gid, access mode
pub fn metadata(self: &NodeRef) -> Result<Metadata, Error> {
if self.flags.contains(NodeFlags::IN_MEMORY_PROPS) {
let props = self.props.lock();
return Ok(props.metadata.unwrap());
}
let mut cache = self.props.lock();
self.data_as_common().metadata(self)
let metadata = cache
.metadata
.get_or_try_insert_with(|| self.data_as_common().metadata(self))?;
Ok(metadata.clone())
}
// TODO clarify directory size

View File

@ -11,7 +11,6 @@ use crate::vfs::file::{DirectoryOpenPosition, InstanceData};
use super::{Metadata, NodeRef};
/// Common interface shared by all filesystem nodes
#[allow(unused)]
pub trait CommonImpl: Send + Sync {
/// Returns `&self` as a reference to `dyn Any`
fn as_any(&self) -> &dyn Any {
@ -20,11 +19,19 @@ pub trait CommonImpl: Send + Sync {
/// Fetches the metadata of the file from underlying storage
fn metadata(&self, node: &NodeRef) -> Result<Metadata, Error> {
let _ = node;
unreachable!("Kernel bug: .metadata() not implemented and no IN_MEMORY_PROPS set")
}
fn set_metadata(&self, node: &NodeRef, metadata: &Metadata) -> Result<(), Error> {
let _ = node;
let _ = metadata;
unreachable!("Kernel bug: .set_metadata() not implemented and no IN_MEMORY_PROPS set")
}
/// Fetches the size of the file from underlying storage
fn size(&self, node: &NodeRef) -> Result<u64, Error> {
let _ = node;
Err(Error::NotImplemented)
}
}

View File

@ -29,4 +29,5 @@ enum Error(u32) {
DirectoryNotEmpty = 26,
NotConnected = 27,
ProcessNotFound = 28,
CrossDeviceLink = 29,
}

View File

@ -1,15 +1,77 @@
use std::path::PathBuf;
#![feature(io_error_more)]
use std::{
fs, io,
path::{Path, PathBuf},
process::ExitCode,
};
use clap::Parser;
#[derive(Debug, Parser)]
struct Args {
#[clap(short, long)]
verbose: bool,
#[clap(short, long)]
no_copy: bool,
src: PathBuf,
dst: PathBuf
dst: PathBuf,
}
fn main() {
fn run(args: &Args) -> Result<(), io::Error> {
let src: &Path = args.src.as_ref();
let dst: &Path = args.dst.as_ref();
if args.verbose {
eprintln!("mv: rename {} -> {}", src.display(), dst.display());
}
match fs::rename(src, dst) {
Ok(()) => return Ok(()),
Err(e) if e.kind() == io::ErrorKind::CrossesDevices => {
if args.no_copy {
if args.verbose {
eprintln!("mv: --no-copy is specified and a move is a cross-device operation");
}
return Err(e);
}
}
Err(e) => return Err(e),
}
if src.is_file() {
let filename = src.file_name().unwrap();
let dst = if dst.is_dir() {
dst.join(filename)
} else {
dst.into()
};
if args.verbose {
eprintln!("mv: copy {} -> {}", src.display(), dst.display());
}
fs::copy(src, dst)?;
if args.verbose {
eprintln!("mv: rm {}", src.display());
}
fs::remove_file(src)?;
} else {
// TODO mv symlink, mv dir
todo!()
}
Ok(())
}
fn main() -> ExitCode {
let args = Args::parse();
std::fs::rename(args.src, args.dst).unwrap();
match run(&args) {
Ok(()) => ExitCode::SUCCESS,
Err(error) => {
eprintln!("{}: {error}", args.src.display());
ExitCode::FAILURE
}
}
}