ext2: metadata update
This commit is contained in:
parent
ce8600a782
commit
69c672bfca
@ -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"
|
||||
|
@ -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) }
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -29,4 +29,5 @@ enum Error(u32) {
|
||||
DirectoryNotEmpty = 26,
|
||||
NotConnected = 27,
|
||||
ProcessNotFound = 28,
|
||||
CrossDeviceLink = 29,
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user