Add support for mkdir

This commit is contained in:
Olivier 'reivilibre' 2022-02-07 23:14:42 +00:00
parent 300baef5c5
commit fecdaaecdf
5 changed files with 94 additions and 13 deletions

View File

@ -259,11 +259,46 @@ impl Filesystem for OliveFilesystem {
umask: u32,
reply: ReplyEntry,
) {
debug!(
"[Not Implemented] mkdir(parent: {:#x?}, name: {:?}, mode: {}, umask: {:#x?})",
parent, name, mode, umask
let name = if let Some(name) = name.to_str() {
name
} else {
// If we can't decode the filename, pretend it doesn't exist.
error!("mkdir: 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.mkdir(parent_vnode, name).await? {
DataResponse::Success(metadata) => {
// Use the direct I/O flag so that we can be in control of when EOF happens
// rather than letting the kernel assume the getattr size is valid
// and caching up to that point.
// We might wind up wanting to do our own buffering...
let file_attr: FileAttr = metadata.into();
// TODO generation should be generated client-side
reply.entry(&Duration::from_secs(5), &file_attr, 42);
}
DataResponse::Error { code, message } => {
warn!("mkdir(parent: {:#x?}) failed: {:?}", parent_vnode, message);
reply.error(code as c_int);
}
}
Ok(())
},
"mkdir",
);
reply.error(ENOSYS);
}
/// Remove a file.

View File

@ -212,6 +212,16 @@ impl Requester {
.await
}
pub async fn mkdir(
&self,
dir_vnode: VnodeId,
name: String,
) -> anyhow::Result<DataResponse<FileMetadata>> {
self.internal
.command(&DataCommand::MakeDirectory { dir_vnode, name })
.await
}
pub async fn open(&self, vnode: VnodeId, mode: OpenMode) -> anyhow::Result<DataResponse<u32>> {
self.internal
.command(&DataCommand::OpenFile { vnode, mode })

View File

@ -91,6 +91,10 @@ pub enum DataCommand {
StatFs {
vnode: VnodeId,
},
MakeDirectory {
dir_vnode: VnodeId,
name: String,
},
}
#[derive(Serialize, Deserialize, Debug, Clone)]

View File

@ -136,6 +136,16 @@ pub async fn handle_command_stream(
)
.await?;
}
DataCommand::MakeDirectory { dir_vnode, name } => {
send_bare_message(
&mut tx,
&file_access
.mkdir(dir_vnode, name)
.await
.unwrap_or_else(error_to_response),
)
.await?;
}
}
}

View File

@ -257,15 +257,17 @@ impl FileAccess {
pub async fn getattr(&self, vnode: VnodeId) -> anyhow::Result<DataResponse<FileMetadata>> {
let inode_map = self.client_state.inode_map.read().await;
if let Some(inode_info) = inode_map.get(vnode.0 as usize) {
let result = self
.read_metadata(&inode_info.real_path, vnode)
.await;
trace!("getattr. Inode {:?} Path: {:?} = {:?}", vnode.0, inode_info.real_path, result);
Ok(result
.map_or_else(
|err| io_error_to_response(err, ENOENT, "getattr"),
|fm| DataResponse::Success(fm),
))
let result = self.read_metadata(&inode_info.real_path, vnode).await;
trace!(
"getattr. Inode {:?} Path: {:?} = {:?}",
vnode.0,
inode_info.real_path,
result
);
Ok(result.map_or_else(
|err| io_error_to_response(err, ENOENT, "getattr"),
|fm| DataResponse::Success(fm),
))
} else {
Ok(DataResponse::Error {
code: error_codes::EFAULT,
@ -416,6 +418,7 @@ impl FileAccess {
name: String,
) -> anyhow::Result<DataResponse<(u32, FileMetadata)>> {
// TODO use the better version of this then handle I/O errors in the callsite
// TODO THIS DOES NOT RESOLVE SYMLINKS THAT IT COULD DO SAFELY
match self.resolve_vnode(dir_vnode).await {
Ok(dir_path) => {
let lookup_path = dir_path.join(name);
@ -740,4 +743,23 @@ impl FileAccess {
statfs_wrapper(&path).map(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 mkdir(
&self,
dir_vnode: VnodeId,
name: String,
) -> anyhow::Result<DataResponse<FileMetadata>> {
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::create_dir(&target_path).await?;
let vnode = self.allocate_vnode(target_path.clone()).await?;
let metadata = self.read_metadata(&target_path, vnode).await?;
Ok(DataResponse::Success(metadata))
}
}