fs: implement rename()
This commit is contained in:
parent
1d58b77241
commit
8c96a009ad
@ -4,10 +4,7 @@ use alloc::sync::Arc;
|
||||
use libk::{
|
||||
block,
|
||||
error::Error,
|
||||
vfs::{
|
||||
CommonImpl, DirectoryImpl, DirectoryOpenPosition, Filesystem, Metadata, Node, NodeFlags,
|
||||
NodeRef,
|
||||
},
|
||||
vfs::{CommonImpl, DirectoryImpl, DirectoryOpenPosition, Metadata, Node, NodeFlags, NodeRef},
|
||||
};
|
||||
use yggdrasil_abi::{
|
||||
io::{DirectoryEntry, FileType},
|
||||
@ -148,7 +145,15 @@ impl<'a> Iterator for DirentIter<'a> {
|
||||
|
||||
impl DirectoryNode {
|
||||
pub fn new(fs: Arc<Ext2Fs>, inode: InodeAccess) -> NodeRef {
|
||||
Node::directory(Self { fs, inode }, NodeFlags::empty(), None)
|
||||
Node::directory(
|
||||
Self {
|
||||
fs: fs.clone(),
|
||||
inode,
|
||||
},
|
||||
NodeFlags::empty(),
|
||||
None,
|
||||
Some(fs),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn create(
|
||||
@ -157,7 +162,10 @@ impl DirectoryNode {
|
||||
parent_ino: Option<u32>,
|
||||
) -> Result<NodeRef, Error> {
|
||||
let ino = inode.ino();
|
||||
let this = Self { fs, inode };
|
||||
let this = Self {
|
||||
fs: fs.clone(),
|
||||
inode,
|
||||
};
|
||||
|
||||
// fsck wants . as first entry, .. as second
|
||||
this.create_entry(".", ino).await?;
|
||||
@ -165,7 +173,7 @@ impl DirectoryNode {
|
||||
this.create_entry("..", parent).await?;
|
||||
}
|
||||
|
||||
Ok(Node::directory(this, NodeFlags::empty(), None))
|
||||
Ok(Node::directory(this, NodeFlags::empty(), None, Some(fs)))
|
||||
}
|
||||
|
||||
async fn create_entry(&self, name: &str, ino: u32) -> Result<(), Error> {
|
||||
@ -360,10 +368,6 @@ impl CommonImpl for DirectoryNode {
|
||||
self
|
||||
}
|
||||
|
||||
fn filesystem(&self) -> Option<&dyn Filesystem> {
|
||||
Some(self.fs.as_ref())
|
||||
}
|
||||
|
||||
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
|
||||
let inode = block!(self.inode.get().await)??;
|
||||
let inode = inode.read();
|
||||
|
@ -17,7 +17,15 @@ pub struct RegularNode {
|
||||
|
||||
impl RegularNode {
|
||||
pub fn new(fs: Arc<Ext2Fs>, inode: InodeAccess) -> NodeRef {
|
||||
Node::regular(Self { fs, inode }, NodeFlags::empty(), None)
|
||||
Node::regular(
|
||||
Self {
|
||||
fs: fs.clone(),
|
||||
inode,
|
||||
},
|
||||
NodeFlags::empty(),
|
||||
None,
|
||||
Some(fs),
|
||||
)
|
||||
}
|
||||
|
||||
async fn resize(&self, new_size: u64) -> Result<(), Error> {
|
||||
|
@ -20,12 +20,13 @@ impl SymlinkNode {
|
||||
pub fn new(fs: Arc<Ext2Fs>, inode: InodeAccess) -> NodeRef {
|
||||
Node::symlink(
|
||||
Self {
|
||||
fs,
|
||||
fs: fs.clone(),
|
||||
inode,
|
||||
cache: IrqSafeRwLock::new(Vec::new()),
|
||||
},
|
||||
NodeFlags::empty(),
|
||||
None,
|
||||
Some(fs),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use core::marker::PhantomData;
|
||||
use alloc::sync::Arc;
|
||||
|
||||
use libk::vfs::{
|
||||
CommonImpl, DirectoryImpl, DirectoryOpenPosition, Metadata, Node, NodeFlags, NodeRef,
|
||||
@ -8,18 +8,19 @@ use yggdrasil_abi::{
|
||||
io::{FileMode, FileType},
|
||||
};
|
||||
|
||||
use crate::{block::BlockAllocator, file::FileNode};
|
||||
use crate::{block::BlockAllocator, file::FileNode, MemoryFilesystem};
|
||||
|
||||
pub(crate) struct DirectoryNode<A: BlockAllocator> {
|
||||
_pd: PhantomData<A>,
|
||||
fs: Arc<MemoryFilesystem<A>>,
|
||||
}
|
||||
|
||||
impl<A: BlockAllocator> DirectoryNode<A> {
|
||||
pub fn new(metadata: Metadata) -> NodeRef {
|
||||
pub fn new(fs: Arc<MemoryFilesystem<A>>, metadata: Metadata) -> NodeRef {
|
||||
Node::directory(
|
||||
Self { _pd: PhantomData },
|
||||
Self { fs: fs.clone() },
|
||||
NodeFlags::IN_MEMORY_SIZE | NodeFlags::IN_MEMORY_PROPS,
|
||||
Some(metadata),
|
||||
Some(fs),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -33,10 +34,14 @@ impl<A: BlockAllocator> DirectoryImpl for DirectoryNode<A> {
|
||||
|
||||
fn create_node(&self, _parent: &NodeRef, ty: FileType) -> Result<NodeRef, Error> {
|
||||
match ty {
|
||||
FileType::File => Ok(FileNode::<A>::new(Metadata::now_root(FileMode::new(0o644)))),
|
||||
FileType::Directory => Ok(DirectoryNode::<A>::new(Metadata::now_root(FileMode::new(
|
||||
0o755,
|
||||
)))),
|
||||
FileType::File => Ok(FileNode::<A>::new(
|
||||
self.fs.clone(),
|
||||
Metadata::now_root(FileMode::new(0o644)),
|
||||
)),
|
||||
FileType::Directory => Ok(DirectoryNode::<A>::new(
|
||||
self.fs.clone(),
|
||||
Metadata::now_root(FileMode::new(0o755)),
|
||||
)),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,25 @@
|
||||
use alloc::sync::Arc;
|
||||
use core::any::Any;
|
||||
|
||||
use libk::vfs::{CommonImpl, InstanceData, Metadata, Node, NodeFlags, NodeRef, RegularImpl};
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
use yggdrasil_abi::{error::Error, io::OpenOptions};
|
||||
|
||||
use crate::{block::BlockAllocator, bvec::BVec};
|
||||
use crate::{block::BlockAllocator, bvec::BVec, MemoryFilesystem};
|
||||
|
||||
pub(crate) struct FileNode<A: BlockAllocator> {
|
||||
pub(crate) data: IrqSafeSpinlock<BVec<'static, A>>,
|
||||
}
|
||||
|
||||
impl<A: BlockAllocator> FileNode<A> {
|
||||
pub fn new(metadata: Metadata) -> NodeRef {
|
||||
pub fn new(fs: Arc<MemoryFilesystem<A>>, metadata: Metadata) -> NodeRef {
|
||||
Node::regular(
|
||||
Self {
|
||||
data: IrqSafeSpinlock::new(BVec::new()),
|
||||
},
|
||||
NodeFlags::IN_MEMORY_PROPS,
|
||||
Some(metadata),
|
||||
Some(fs),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -4,13 +4,14 @@
|
||||
#![allow(clippy::new_without_default, clippy::new_ret_no_self)]
|
||||
#![feature(maybe_uninit_uninit_array, maybe_uninit_array_assume_init)]
|
||||
|
||||
use core::{cell::RefCell, marker::PhantomData};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use alloc::rc::Rc;
|
||||
use alloc::sync::Arc;
|
||||
use block::BlockAllocator;
|
||||
use dir::DirectoryNode;
|
||||
use file::FileNode;
|
||||
use libk::vfs::{impls::fixed_path_symlink, AccessToken, Metadata, NodeRef};
|
||||
use libk::vfs::{impls::fixed_path_symlink, AccessToken, Filesystem, Metadata, NodeRef};
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
use tar::TarEntry;
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
@ -58,13 +59,13 @@ mod tar;
|
||||
|
||||
/// In-memory read/write filesystem
|
||||
pub struct MemoryFilesystem<A: BlockAllocator> {
|
||||
root: RefCell<Option<NodeRef>>,
|
||||
root: IrqSafeSpinlock<Option<NodeRef>>,
|
||||
_pd: PhantomData<A>,
|
||||
}
|
||||
|
||||
impl<A: BlockAllocator> MemoryFilesystem<A> {
|
||||
fn make_path(
|
||||
// self: &Rc<Self>,
|
||||
self: &Arc<Self>,
|
||||
at: &NodeRef,
|
||||
path: &Path,
|
||||
create: bool,
|
||||
@ -88,7 +89,7 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
|
||||
return Err(Error::DoesNotExist);
|
||||
}
|
||||
|
||||
let node = DirectoryNode::<A>::new(Metadata::now_root(mode));
|
||||
let node = DirectoryNode::<A>::new(self.clone(), Metadata::now_root(mode));
|
||||
at.add_child(element, node.clone())?;
|
||||
|
||||
node
|
||||
@ -103,17 +104,20 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
|
||||
Ok(node)
|
||||
} else {
|
||||
assert!(node.is_directory());
|
||||
Self::make_path(&node, rest, create, mode)
|
||||
self.make_path(&node, rest, create, mode)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_node_initial(self: &Rc<Self>, hdr: &TarEntry) -> Result<NodeRef, Error> {
|
||||
fn create_node_initial(self: &Arc<Self>, hdr: &TarEntry) -> Result<NodeRef, Error> {
|
||||
let kind = hdr.node_kind();
|
||||
let mode = usize::from(&hdr.mode);
|
||||
let mode = FileMode::new(0o777 & (mode as u32));
|
||||
match kind {
|
||||
FileType::File => Ok(FileNode::<A>::new(Metadata::now_root(mode))),
|
||||
FileType::Directory => Ok(DirectoryNode::<A>::new(Metadata::now_root(mode))),
|
||||
FileType::File => Ok(FileNode::<A>::new(self.clone(), Metadata::now_root(mode))),
|
||||
FileType::Directory => Ok(DirectoryNode::<A>::new(
|
||||
self.clone(),
|
||||
Metadata::now_root(mode),
|
||||
)),
|
||||
FileType::Symlink => {
|
||||
let target = hdr.symlink_target()?;
|
||||
Ok(fixed_path_symlink(target))
|
||||
@ -122,8 +126,8 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
|
||||
}
|
||||
}
|
||||
|
||||
fn from_slice_internal(self: &Rc<Self>, tar_data: &'static [u8]) -> Result<NodeRef, Error> {
|
||||
let root = DirectoryNode::<A>::new(Metadata::now_root(FileMode::new(0o755)));
|
||||
fn from_slice_internal(self: &Arc<Self>, tar_data: &'static [u8]) -> Result<NodeRef, Error> {
|
||||
let root = DirectoryNode::<A>::new(self.clone(), Metadata::now_root(FileMode::new(0o755)));
|
||||
|
||||
// 1. Create paths in tar
|
||||
for item in TarIterator::new(tar_data) {
|
||||
@ -134,7 +138,7 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
|
||||
let path = Path::from_str(hdr.name.as_str()?.trim_matches('/'));
|
||||
|
||||
let (dirname, filename) = path.split_right();
|
||||
let parent = Self::make_path(&root, dirname, true, FileMode::new(0o755))?;
|
||||
let parent = self.make_path(&root, dirname, true, FileMode::new(0o755))?;
|
||||
let node = self.create_node_initial(hdr)?;
|
||||
|
||||
parent.add_child(filename, node)?;
|
||||
@ -147,7 +151,7 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
|
||||
};
|
||||
|
||||
let path = Path::from_str(hdr.name.as_str()?.trim_matches('/'));
|
||||
let node = Self::make_path(&root, path, false, FileMode::empty())?;
|
||||
let node = self.make_path(&root, path, false, FileMode::empty())?;
|
||||
assert_eq!(node.ty(), hdr.node_kind());
|
||||
|
||||
let uid = unsafe { UserId::from_raw(usize::from(&hdr.uid) as u32) };
|
||||
@ -171,9 +175,9 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
|
||||
}
|
||||
|
||||
/// Constructs a filesystem tree from a tar image in memory
|
||||
pub fn from_slice(tar_data: &'static [u8]) -> Result<Rc<Self>, Error> {
|
||||
let fs = Rc::new(Self {
|
||||
root: RefCell::new(None),
|
||||
pub fn from_slice(tar_data: &'static [u8]) -> Result<Arc<Self>, Error> {
|
||||
let fs = Arc::new(Self {
|
||||
root: IrqSafeSpinlock::new(None),
|
||||
_pd: PhantomData,
|
||||
});
|
||||
let root = fs.from_slice_internal(tar_data)?;
|
||||
@ -185,7 +189,13 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
|
||||
// TODO Filesystem trait?
|
||||
/// Returns the root node of the memory filesystem
|
||||
pub fn root(&self) -> Result<NodeRef, Error> {
|
||||
Ok(self.root.borrow().clone().unwrap())
|
||||
Ok(self.root.lock().clone().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: BlockAllocator> Filesystem for MemoryFilesystem<A> {
|
||||
fn display_name(&self) -> &'static str {
|
||||
"memfs"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,12 @@ pub fn add_named_char_device(
|
||||
) -> Result<(), Error> {
|
||||
log::info!("Add char device: {}", name);
|
||||
|
||||
let node = Node::char(dev, NodeFlags::IN_MEMORY_PROPS, Metadata::now_root(mode));
|
||||
let node = Node::char(
|
||||
dev,
|
||||
NodeFlags::IN_MEMORY_PROPS,
|
||||
Metadata::now_root(mode),
|
||||
None,
|
||||
);
|
||||
|
||||
DEVFS_ROOT.get().add_child(name, node)
|
||||
}
|
||||
@ -60,7 +65,12 @@ pub fn add_named_block_device<S: Into<String>>(
|
||||
let name = name.into();
|
||||
log::info!("Add block device: {}", name);
|
||||
|
||||
let node = Node::block(dev, NodeFlags::IN_MEMORY_PROPS, Metadata::now_root(mode));
|
||||
let node = Node::block(
|
||||
dev,
|
||||
NodeFlags::IN_MEMORY_PROPS,
|
||||
Metadata::now_root(mode),
|
||||
None,
|
||||
);
|
||||
|
||||
DEVFS_ROOT.get().add_child(name, node.clone())?;
|
||||
|
||||
|
@ -119,6 +119,7 @@ impl<V: BytesAttributeOps> Attribute<V::Data> for BytesAttribute<V> {
|
||||
},
|
||||
NodeFlags::IN_MEMORY_PROPS,
|
||||
Some(Metadata::now_root(mode)),
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -187,6 +187,7 @@ impl<V: StringAttributeOps> Attribute<V::Data> for StringAttribute<V> {
|
||||
},
|
||||
NodeFlags::IN_MEMORY_PROPS,
|
||||
Some(Metadata::now_root(mode)),
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ impl<D> KObject<D> {
|
||||
MemoryDirectory,
|
||||
NodeFlags::IN_MEMORY_SIZE | NodeFlags::IN_MEMORY_PROPS,
|
||||
Some(Metadata::now_root(FileMode::new(0o555))),
|
||||
None,
|
||||
);
|
||||
Arc::new(Self { data, node })
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use yggdrasil_abi::error::Error;
|
||||
use super::NodeRef;
|
||||
|
||||
#[async_trait]
|
||||
pub trait Filesystem: Sync {
|
||||
pub trait Filesystem: Sync + Send {
|
||||
async fn flush(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ use crate::vfs::{
|
||||
FileRef, NodeRef,
|
||||
};
|
||||
|
||||
use super::Node;
|
||||
|
||||
/// Describes a general filesystem access
|
||||
pub enum Action {
|
||||
/// Access involves reading data without modification
|
||||
@ -283,6 +285,59 @@ impl IoContext {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rename(
|
||||
&mut self,
|
||||
src_at: Option<NodeRef>,
|
||||
src: impl AsRef<Path>,
|
||||
dst_at: Option<NodeRef>,
|
||||
dst: impl AsRef<Path>,
|
||||
) -> Result<(), Error> {
|
||||
let src = src.as_ref();
|
||||
let dst = dst.as_ref();
|
||||
|
||||
let (dst_parent, dst_name) = dst.split_dirname();
|
||||
let (_, src_name) = src.split_dirname();
|
||||
|
||||
if src_name.is_empty() {
|
||||
log::warn!("Unhandled src move path: {src:?}");
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
let src_node = self.find(src_at, src, false)?;
|
||||
if src_node.is_root() {
|
||||
log::warn!("Cannot move root path");
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
let src_parent = src_node.parent();
|
||||
|
||||
// Try to find destination node first
|
||||
let (dst_node, dst_name) = if let Ok(node) = self.find(dst_at.clone(), dst, false) {
|
||||
// There is a node with the destination name, but it's not a directory
|
||||
if !node.is_directory() {
|
||||
return Err(Error::AlreadyExists);
|
||||
}
|
||||
|
||||
// mv /a/b /c if /c exists -> move /a/b into /c with filename b
|
||||
(node, src_name)
|
||||
} else {
|
||||
// Otherwise try looking up a parent
|
||||
let node = self.find(dst_at, dst_parent, true)?;
|
||||
if !node.is_directory() {
|
||||
return Err(Error::NotADirectory);
|
||||
}
|
||||
|
||||
// mv /a/b /c/d if /c/d doesn't exist -> move /a/b into /c with filename d
|
||||
(node, dst_name)
|
||||
};
|
||||
|
||||
let access = self.check_access(&src_node, AccessMode::READ | AccessMode::WRITE)?
|
||||
+ self.check_access(&dst_node, AccessMode::WRITE)?;
|
||||
|
||||
Node::move_node(&src_parent, &dst_node, src_name, dst_name, access)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes a device or regular file node at given path
|
||||
pub fn remove_file<P: AsRef<Path>>(
|
||||
&mut self,
|
||||
|
@ -130,6 +130,7 @@ where
|
||||
Self::new(read),
|
||||
NodeFlags::IN_MEMORY_PROPS,
|
||||
Some(Metadata::now_root(FileMode::new(0o444))),
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -233,6 +234,7 @@ where
|
||||
Self::new(read, write),
|
||||
NodeFlags::IN_MEMORY_PROPS,
|
||||
Some(Metadata::now_root(FileMode::new(0o644))),
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -373,6 +375,7 @@ impl MemoryDirectory {
|
||||
MemoryDirectory,
|
||||
NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE,
|
||||
Some(Metadata::now_root(mode)),
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -456,6 +459,7 @@ pub fn read_fn_node<R: ReadFn + 'static>(read: R) -> NodeRef {
|
||||
ReadOnlyFnNode::new(read),
|
||||
NodeFlags::IN_MEMORY_PROPS,
|
||||
Some(Metadata::now_root(FileMode::new(0o444))),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
@ -465,6 +469,7 @@ pub fn mdir<S: Into<String>, I: IntoIterator<Item = (S, NodeRef)>>(it: I) -> Nod
|
||||
MemoryDirectory,
|
||||
NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE,
|
||||
Some(Metadata::now_root(FileMode::new(0o555))),
|
||||
None,
|
||||
);
|
||||
for (name, node) in it {
|
||||
dir.add_child(name, node).unwrap();
|
||||
@ -479,6 +484,7 @@ pub fn fixed_path_symlink(target: impl Into<String>) -> NodeRef {
|
||||
},
|
||||
NodeFlags::IN_MEMORY_PROPS,
|
||||
Some(Metadata::now_root(FileMode::new(0o555))),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -132,6 +132,7 @@ struct PropertyCache {
|
||||
pub struct Node {
|
||||
data: NodeImpl,
|
||||
flags: NodeFlags,
|
||||
filesystem: Option<Arc<dyn Filesystem>>,
|
||||
props: IrqSafeSpinlock<PropertyCache>,
|
||||
parent: IrqSafeSpinlock<Option<NodeRef>>,
|
||||
}
|
||||
@ -157,7 +158,12 @@ impl Metadata {
|
||||
}
|
||||
|
||||
impl Node {
|
||||
fn new(data: NodeImpl, flags: NodeFlags, metadata: Option<Metadata>) -> NodeRef {
|
||||
fn new(
|
||||
data: NodeImpl,
|
||||
flags: NodeFlags,
|
||||
metadata: Option<Metadata>,
|
||||
filesystem: Option<Arc<dyn Filesystem>>,
|
||||
) -> NodeRef {
|
||||
Arc::new(Self {
|
||||
data,
|
||||
flags,
|
||||
@ -165,6 +171,7 @@ impl Node {
|
||||
metadata,
|
||||
size: None,
|
||||
}),
|
||||
filesystem,
|
||||
parent: IrqSafeSpinlock::new(None),
|
||||
})
|
||||
}
|
||||
@ -178,11 +185,13 @@ impl Node {
|
||||
NodeImpl::PseudoTerminalMaster(Arc::downgrade(master)),
|
||||
NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE,
|
||||
Some(metadata),
|
||||
None,
|
||||
);
|
||||
let slave = Self::new(
|
||||
NodeImpl::PseudoTerminalSlave(Arc::downgrade(slave)),
|
||||
NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE,
|
||||
Some(metadata),
|
||||
None,
|
||||
);
|
||||
|
||||
master.props.lock().size = Some(0);
|
||||
@ -196,13 +205,14 @@ impl Node {
|
||||
data: T,
|
||||
flags: NodeFlags,
|
||||
metadata: Option<Metadata>,
|
||||
filesystem: Option<Arc<dyn Filesystem>>,
|
||||
) -> NodeRef {
|
||||
let data = NodeImpl::Directory(DirectoryData {
|
||||
imp: Box::new(data),
|
||||
mountpoint: IrqSafeSpinlock::new(None),
|
||||
children: IrqSafeSpinlock::new(Vec::new()),
|
||||
});
|
||||
Self::new(data, flags, metadata)
|
||||
Self::new(data, flags, metadata, filesystem)
|
||||
}
|
||||
|
||||
// /// Creates a new directory node with given [DirectoryImpl]
|
||||
@ -215,8 +225,14 @@ impl Node {
|
||||
data: T,
|
||||
flags: NodeFlags,
|
||||
metadata: Option<Metadata>,
|
||||
filesystem: Option<Arc<dyn Filesystem>>,
|
||||
) -> NodeRef {
|
||||
Self::new(NodeImpl::Regular(Box::new(data)), flags, metadata)
|
||||
Self::new(
|
||||
NodeImpl::Regular(Box::new(data)),
|
||||
flags,
|
||||
metadata,
|
||||
filesystem,
|
||||
)
|
||||
}
|
||||
|
||||
// /// Creates a new file node with given [RegularImpl]
|
||||
@ -225,20 +241,32 @@ impl Node {
|
||||
// }
|
||||
|
||||
/// Creates a new block device node with given [BlockDevice]
|
||||
pub fn block(device: Arc<dyn BlockDevice>, flags: NodeFlags, metadata: Metadata) -> NodeRef {
|
||||
pub fn block(
|
||||
device: Arc<dyn BlockDevice>,
|
||||
flags: NodeFlags,
|
||||
metadata: Metadata,
|
||||
filesystem: Option<Arc<dyn Filesystem>>,
|
||||
) -> NodeRef {
|
||||
Self::new(
|
||||
NodeImpl::Block(BlockDeviceFile(device)),
|
||||
flags,
|
||||
Some(metadata),
|
||||
filesystem,
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a new character device node with given [CharDevice]
|
||||
pub fn char(device: Arc<dyn CharDevice>, flags: NodeFlags, metadata: Metadata) -> NodeRef {
|
||||
pub fn char(
|
||||
device: Arc<dyn CharDevice>,
|
||||
flags: NodeFlags,
|
||||
metadata: Metadata,
|
||||
filesystem: Option<Arc<dyn Filesystem>>,
|
||||
) -> NodeRef {
|
||||
Self::new(
|
||||
NodeImpl::Char(CharDeviceFile(device)),
|
||||
flags,
|
||||
Some(metadata),
|
||||
filesystem,
|
||||
)
|
||||
}
|
||||
|
||||
@ -247,6 +275,7 @@ impl Node {
|
||||
data: T,
|
||||
flags: NodeFlags,
|
||||
metadata: Option<Metadata>,
|
||||
filesystem: Option<Arc<dyn Filesystem>>,
|
||||
) -> NodeRef {
|
||||
Self::new(
|
||||
NodeImpl::Symlink(SymlinkData {
|
||||
@ -255,6 +284,7 @@ impl Node {
|
||||
}),
|
||||
flags,
|
||||
metadata,
|
||||
filesystem,
|
||||
)
|
||||
}
|
||||
|
||||
@ -266,6 +296,7 @@ impl Node {
|
||||
}),
|
||||
NodeFlags::IN_MEMORY_SIZE,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
@ -286,8 +317,8 @@ impl Node {
|
||||
}
|
||||
|
||||
/// Returns a [Filesystem] this node belongs to.
|
||||
pub fn filesystem(&self) -> Option<&dyn Filesystem> {
|
||||
self.data_as_common().filesystem()
|
||||
pub fn filesystem(&self) -> Option<Arc<dyn Filesystem>> {
|
||||
self.filesystem.clone()
|
||||
}
|
||||
|
||||
/// Returns the impl data of the node as `dyn Any`
|
||||
@ -355,6 +386,14 @@ impl Node {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_same_filesystem(a: &Self, b: &Self) -> bool {
|
||||
if let (Some(fs_a), Some(fs_b)) = (a.filesystem.as_ref(), b.filesystem.as_ref()) {
|
||||
Arc::ptr_eq(fs_a, fs_b)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn mountpoint_target(&self) -> Option<NodeRef> {
|
||||
match &self.data {
|
||||
NodeImpl::Directory(dir) => dir.mountpoint.lock().clone(),
|
||||
@ -476,16 +515,16 @@ mod tests {
|
||||
fn node_sync_send() {
|
||||
fn node_send<T: Send>(_n: &T) {}
|
||||
|
||||
let node = Node::regular(DummyFile, NodeFlags::empty());
|
||||
let node = Node::regular(DummyFile, NodeFlags::empty(), None, None);
|
||||
|
||||
node_send(&node);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dir_cache_add() {
|
||||
let d1 = Node::directory(DummyDirectory, NodeFlags::empty(), None);
|
||||
let d2 = Node::directory(DummyDirectory, NodeFlags::empty(), None);
|
||||
let f1 = Node::regular(DummyFile, NodeFlags::empty());
|
||||
let d1 = Node::directory(DummyDirectory, NodeFlags::empty(), None, None);
|
||||
let d2 = Node::directory(DummyDirectory, NodeFlags::empty(), None, None);
|
||||
let f1 = Node::regular(DummyFile, NodeFlags::empty(), None, None);
|
||||
|
||||
assert!(Arc::ptr_eq(&f1.parent(), &f1));
|
||||
assert_eq!(d1.children_len().unwrap(), 0);
|
||||
@ -504,11 +543,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn in_mem_dir_size_coherence() {
|
||||
let d = Node::directory(DummyDirectory, NodeFlags::IN_MEMORY_SIZE, None);
|
||||
let d = Node::directory(DummyDirectory, NodeFlags::IN_MEMORY_SIZE, None, None);
|
||||
|
||||
for i in 0..10 {
|
||||
let name = format!("f{}", i);
|
||||
let node = Node::regular(DummyFile, NodeFlags::empty());
|
||||
let node = Node::regular(DummyFile, NodeFlags::empty(), None, None);
|
||||
|
||||
d.add_child(name, node).unwrap();
|
||||
assert_eq!(d.size().unwrap(), d.children_len().unwrap() as u64);
|
||||
@ -528,7 +567,7 @@ mod tests {
|
||||
}
|
||||
impl DirectoryImpl for AnyData {}
|
||||
|
||||
let d = Node::directory(AnyData { value: 1234 }, NodeFlags::empty(), None);
|
||||
let d = Node::directory(AnyData { value: 1234 }, NodeFlags::empty(), None, None);
|
||||
let r = d.data_as_ref::<AnyData>();
|
||||
|
||||
assert_eq!(r.value, 1234);
|
||||
|
@ -8,7 +8,7 @@ use yggdrasil_abi::{
|
||||
|
||||
use crate::vfs::file::{File, FileRef};
|
||||
|
||||
use super::{AccessToken, CreateInfo, Metadata, Node, NodeFlags, NodeImpl, NodeRef};
|
||||
use super::{AccessToken, CreateInfo, DirectoryData, Metadata, Node, NodeFlags, NodeImpl, NodeRef};
|
||||
|
||||
impl Node {
|
||||
// Devices
|
||||
@ -159,6 +159,50 @@ impl Node {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn move_node(
|
||||
source: &NodeRef,
|
||||
destination: &NodeRef,
|
||||
source_name: &str,
|
||||
destination_name: &str,
|
||||
access: AccessToken,
|
||||
) -> Result<(), Error> {
|
||||
// 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);
|
||||
}
|
||||
|
||||
let source_dir = source.as_directory()?;
|
||||
let destination_dir = destination.as_directory()?;
|
||||
|
||||
let node = source.lookup_or_load(source_name, access)?;
|
||||
|
||||
match source_dir.imp.unlink_node(source, source_name) {
|
||||
Ok(()) | Err(Error::NotImplemented) => (),
|
||||
Err(error) => {
|
||||
log::warn!("Rename: unlink {source_name}: {error:?}");
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
|
||||
match destination_dir
|
||||
.imp
|
||||
.attach_node(destination, &node, destination_name)
|
||||
{
|
||||
Ok(()) | Err(Error::NotImplemented) => (),
|
||||
Err(error) => {
|
||||
log::warn!("Rename: attach {destination_name}: {error:?}");
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
|
||||
source_dir.remove_child(source_name);
|
||||
destination.add_child(destination_name, node)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Common
|
||||
|
||||
/// Changes user/group ID or access mode of the node
|
||||
@ -245,3 +289,18 @@ impl Node {
|
||||
symlink.imp.read_link(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl DirectoryData {
|
||||
fn remove_child(&self, name: &str) {
|
||||
let mut children = self.children.lock();
|
||||
|
||||
children.retain(|(t, child)| {
|
||||
if t == name {
|
||||
*child.parent.lock() = None;
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -6,10 +6,7 @@ use yggdrasil_abi::{
|
||||
io::{DirectoryEntry, FileType, OpenOptions},
|
||||
};
|
||||
|
||||
use crate::vfs::{
|
||||
file::{DirectoryOpenPosition, InstanceData},
|
||||
Filesystem,
|
||||
};
|
||||
use crate::vfs::file::{DirectoryOpenPosition, InstanceData};
|
||||
|
||||
use super::{Metadata, NodeRef};
|
||||
|
||||
@ -21,11 +18,6 @@ pub trait CommonImpl: Send + Sync {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
/// Returns a [Filesystem] this node belongs to.
|
||||
fn filesystem(&self) -> Option<&dyn Filesystem> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Fetches the metadata of the file from underlying storage
|
||||
fn metadata(&self, node: &NodeRef) -> Result<Metadata, Error> {
|
||||
unreachable!("Kernel bug: .metadata() not implemented and no IN_MEMORY_PROPS set")
|
||||
|
@ -5,7 +5,7 @@ use abi::{
|
||||
io::{
|
||||
AccessMode, ChannelPublisherId, DeviceRequest, DirectoryEntry, FileAttr, FileControl,
|
||||
FileMetadataUpdate, FileMode, FilesystemControl, MessageDestination, OpenOptions,
|
||||
PipeOptions, PollControl, RawFd, ReceivedMessageMetadata, SeekFrom, SentMessage,
|
||||
PipeOptions, PollControl, RawFd, ReceivedMessageMetadata, Rename, SeekFrom, SentMessage,
|
||||
TerminalOptions, TerminalSize, TimerOptions,
|
||||
},
|
||||
process::{ProcessWait, WaitFlags},
|
||||
@ -18,7 +18,7 @@ use libk::{
|
||||
};
|
||||
use libk_util::io::{ReadAt, WriteAt};
|
||||
|
||||
use crate::syscall::{run_with_io, run_with_io_at};
|
||||
use crate::syscall::{at_fd, run_with_io, run_with_io_at};
|
||||
|
||||
// I/O
|
||||
pub(crate) fn open(
|
||||
@ -167,6 +167,18 @@ pub(crate) fn remove(at: Option<RawFd>, path: &str) -> Result<(), Error> {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn rename(rename: &Rename) -> Result<(), Error> {
|
||||
let thread = Thread::current();
|
||||
let process = thread.process();
|
||||
run_with_io(&process, |mut io| {
|
||||
let src_at = at_fd(&io, rename.source_at)?;
|
||||
let dst_at = at_fd(&io, rename.destination_at)?;
|
||||
|
||||
io.ioctx_mut()
|
||||
.rename(src_at, rename.source, dst_at, rename.destination)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn clone_fd(source_fd: RawFd, target_fd: Option<RawFd>) -> Result<RawFd, Error> {
|
||||
let thread = Thread::current();
|
||||
let process = thread.process();
|
||||
|
@ -16,6 +16,15 @@ fn run_with_io<T, F: FnOnce(IrqSafeSpinlockGuard<ProcessIo>) -> T>(proc: &Proces
|
||||
f(io)
|
||||
}
|
||||
|
||||
fn at_fd(io: &ProcessIo, fd: Option<RawFd>) -> Result<Option<NodeRef>, Error> {
|
||||
if let Some(fd) = fd {
|
||||
let file = io.files.file(fd)?;
|
||||
file.node().ok_or(Error::InvalidFile).cloned().map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn run_with_io_at<T, F: FnOnce(NodeRef, IrqSafeSpinlockGuard<ProcessIo>) -> Result<T, Error>>(
|
||||
proc: &Process,
|
||||
at: Option<RawFd>,
|
||||
|
@ -31,6 +31,7 @@ extern {
|
||||
type FileMetadataUpdate = yggdrasil_abi::io::FileMetadataUpdate;
|
||||
type FileControl = yggdrasil_abi::io::FileControl;
|
||||
type FilesystemControl = yggdrasil_abi::io::FilesystemControl;
|
||||
type Rename = yggdrasil_abi::io::Rename;
|
||||
|
||||
type DebugOperation = yggdrasil_abi::debug::DebugOperation;
|
||||
type DebugFrame = yggdrasil_abi::debug::DebugFrame;
|
||||
@ -126,6 +127,7 @@ syscall remove_directory(at: Option<RawFd>, path: &str) -> Result<()>;
|
||||
|
||||
syscall read_link(at: Option<RawFd>, path: &str, buf: &mut [u8]) -> Result<usize>;
|
||||
syscall remove(at: Option<RawFd>, path: &str) -> Result<()>;
|
||||
syscall rename(rename: &Rename<'_>) -> Result<()>;
|
||||
syscall clone_fd(source: RawFd, target: Option<RawFd>) -> Result<RawFd>;
|
||||
syscall update_metadata(at: Option<RawFd>, path: &str, update: &FileMetadataUpdate) -> Result<()>;
|
||||
syscall get_metadata(at: Option<RawFd>, path: &str, metadata: &mut MaybeUninit<FileAttr>, follow: bool) -> Result<()>;
|
||||
|
@ -19,6 +19,14 @@ pub use terminal::{
|
||||
TerminalOutputOptions, TerminalSize,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Rename<'a> {
|
||||
pub source_at: Option<RawFd>,
|
||||
pub source: &'a str,
|
||||
pub destination_at: Option<RawFd>,
|
||||
pub destination: &'a str,
|
||||
}
|
||||
|
||||
impl UserId {
|
||||
pub const fn root() -> Self {
|
||||
Self(0)
|
||||
|
@ -67,6 +67,21 @@ impl Path {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split_dirname(&self) -> (&Path, &str) {
|
||||
if let Some((left, right)) = self.0.rsplit_once(Self::SEPARATOR) {
|
||||
if left.is_empty() {
|
||||
// Cases like "/a" or "a"
|
||||
let left = if self.is_absolute() { "/" } else { "" }.as_ref();
|
||||
|
||||
(left, right)
|
||||
} else {
|
||||
(left.as_ref(), right)
|
||||
}
|
||||
} else {
|
||||
("".as_ref(), self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_absolute(&self) -> bool {
|
||||
self.0.starts_with(Self::SEPARATOR)
|
||||
@ -117,3 +132,28 @@ impl AsRef<Path> for alloc::string::String {
|
||||
self.as_str().as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Path;
|
||||
|
||||
#[test]
|
||||
fn test_path_split_dirname() {
|
||||
let cases = [
|
||||
("/a/b", "/a", "b"),
|
||||
("/a/b/", "/a/b", ""),
|
||||
("/", "/", ""),
|
||||
("", "", ""),
|
||||
("a/b", "a", "b"),
|
||||
("a", "", "a"),
|
||||
("a/b/", "a/b", ""),
|
||||
];
|
||||
|
||||
for (input, out_path, out_name) in cases {
|
||||
let path = Path::from_str(input);
|
||||
let (left, right) = path.split_dirname();
|
||||
assert_eq!(left.as_str(), out_path);
|
||||
assert_eq!(right, out_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ use core::str::FromStr;
|
||||
|
||||
pub use abi::io::{
|
||||
AccessMode, DirectoryEntry, FileAttr, FileMetadataUpdate, FileMetadataUpdateMode, FileMode,
|
||||
FileType, OpenOptions, PipeOptions, RawFd, SeekFrom, TimerOptions,
|
||||
FileType, OpenOptions, PipeOptions, RawFd, Rename, SeekFrom, TimerOptions,
|
||||
};
|
||||
|
||||
use abi::{error::Error, io::DeviceRequest, process::ProcessOption, util::FixedString};
|
||||
|
@ -52,6 +52,10 @@ path = "src/kmod.rs"
|
||||
name = "ls"
|
||||
path = "src/ls.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "mv"
|
||||
path = "src/mv.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "mkdir"
|
||||
path = "src/mkdir.rs"
|
||||
|
15
userspace/sysutils/src/mv.rs
Normal file
15
userspace/sysutils/src/mv.rs
Normal file
@ -0,0 +1,15 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
struct Args {
|
||||
src: PathBuf,
|
||||
dst: PathBuf
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
std::fs::rename(args.src, args.dst).unwrap();
|
||||
}
|
@ -29,6 +29,7 @@ const PROGRAMS: &[(&str, &str)] = &[
|
||||
("mount", "sbin/mount"),
|
||||
("login", "sbin/login"),
|
||||
("ls", "bin/ls"),
|
||||
("mv", "bin/mv"),
|
||||
("mkdir", "bin/mkdir"),
|
||||
("touch", "bin/touch"),
|
||||
("rm", "bin/rm"),
|
||||
|
Loading…
x
Reference in New Issue
Block a user