From e709db8003e7b13b09b46879612cc2b6bd8dc723 Mon Sep 17 00:00:00 2001 From: Olivier 'reivilibre Date: Tue, 8 Feb 2022 06:56:00 +0000 Subject: [PATCH] Add support for rmdir --- olivefs/src/filesystem.rs | 39 ++++++++++++++++++++++++++---- olivefs/src/requester.rs | 10 ++++++++ olivefs_common/src/messages.rs | 4 +++ olivefsd/src/server/connections.rs | 14 +++++++++-- olivefsd/src/server/file_access.rs | 16 ++++++++++++ 5 files changed, 76 insertions(+), 7 deletions(-) diff --git a/olivefs/src/filesystem.rs b/olivefs/src/filesystem.rs index 80d5491..1bae26a 100644 --- a/olivefs/src/filesystem.rs +++ b/olivefs/src/filesystem.rs @@ -307,7 +307,7 @@ impl Filesystem for OliveFilesystem { reply.error(ENOENT); return; } - .to_owned(); + .to_owned(); let requester = self.requester.clone(); @@ -337,11 +337,40 @@ impl Filesystem for OliveFilesystem { /// Remove a directory. fn rmdir(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) { - debug!( - "[Not Implemented] rmdir(parent: {:#x?}, name: {:?})", - parent, name, + let name = if let Some(name) = name.to_str() { + name + } else { + // If we can't decode the filename, pretend it doesn't exist. + error!("rmdir: Filename not convertible."); + reply.error(ENOENT); + return; + } + .to_owned(); + + let requester = self.requester.clone(); + + self.spawn_with_error_handler( + async move { + let parent_vnode = VnodeId( + parent + .try_into() + .context("Converting u64 inode to u32 VnodeId.")?, + ); + + match requester.rmdir(parent_vnode, name).await? { + DataResponse::Success(()) => { + reply.ok(); + } + DataResponse::Error { code, message } => { + warn!("rmdir(parent: {:#x?}) failed: {:?}", parent_vnode, message); + reply.error(code as c_int); + } + } + + Ok(()) + }, + "rmdir", ); - reply.error(ENOSYS); } /// Create a symbolic link. diff --git a/olivefs/src/requester.rs b/olivefs/src/requester.rs index 00c747b..7b5667f 100644 --- a/olivefs/src/requester.rs +++ b/olivefs/src/requester.rs @@ -232,6 +232,16 @@ impl Requester { .await } + pub async fn rmdir( + &self, + dir_vnode: VnodeId, + name: String, + ) -> anyhow::Result> { + self.internal + .command(&DataCommand::RemoveDirectory { dir_vnode, name }) + .await + } + pub async fn open(&self, vnode: VnodeId, mode: OpenMode) -> anyhow::Result> { self.internal .command(&DataCommand::OpenFile { vnode, mode }) diff --git a/olivefs_common/src/messages.rs b/olivefs_common/src/messages.rs index 8419324..aaaf1d6 100644 --- a/olivefs_common/src/messages.rs +++ b/olivefs_common/src/messages.rs @@ -99,6 +99,10 @@ pub enum DataCommand { dir_vnode: VnodeId, name: String, }, + RemoveDirectory { + dir_vnode: VnodeId, + name: String, + }, } #[derive(Serialize, Deserialize, Debug, Clone)] diff --git a/olivefsd/src/server/connections.rs b/olivefsd/src/server/connections.rs index cbe1f38..cb14a4a 100644 --- a/olivefsd/src/server/connections.rs +++ b/olivefsd/src/server/connections.rs @@ -144,7 +144,7 @@ pub async fn handle_command_stream( .await .unwrap_or_else(error_to_response), ) - .await?; + .await?; } DataCommand::Unlink { dir_vnode, name } => { send_bare_message( @@ -154,7 +154,17 @@ pub async fn handle_command_stream( .await .unwrap_or_else(error_to_response), ) - .await?; + .await?; + } + DataCommand::RemoveDirectory { dir_vnode, name } => { + send_bare_message( + &mut tx, + &file_access + .rmdir(dir_vnode, name) + .await + .unwrap_or_else(error_to_response), + ) + .await?; } } } diff --git a/olivefsd/src/server/file_access.rs b/olivefsd/src/server/file_access.rs index 9d1a325..cbeea7d 100644 --- a/olivefsd/src/server/file_access.rs +++ b/olivefsd/src/server/file_access.rs @@ -778,4 +778,20 @@ impl FileAccess { tokio::fs::remove_file(&target_path).await?; Ok(DataResponse::Success(())) } + + /// Symlink safety: + /// Makes an attempt to safely resolve symlinks, but there is a possibility of a TOCTOU race. + /// TODO Revisit later for safety. + pub async fn rmdir( + &self, + dir_vnode: VnodeId, + name: String, + ) -> anyhow::Result> { + let parent_dir_resolved = self + .resolve_vnode_including_follow_symlinks_safely_best_effort(dir_vnode) + .await?; + let target_path = parent_dir_resolved.join(name); + tokio::fs::remove_dir(&target_path).await?; + Ok(DataResponse::Success(())) + } }