Add support for symlink

This commit is contained in:
Olivier 'reivilibre' 2022-02-08 07:37:44 +00:00
parent e709db8003
commit 926a29fa47
5 changed files with 92 additions and 4 deletions

View File

@ -382,11 +382,52 @@ impl Filesystem for OliveFilesystem {
link: &Path,
reply: ReplyEntry,
) {
debug!(
"[Not Implemented] symlink(parent: {:#x?}, name: {:?}, link: {:?})",
parent, name, link,
let name = if let Some(name) = name.to_str() {
name
} else {
// If we can't decode the filename, pretend it doesn't exist.
error!("symlink: Filename not convertible.");
reply.error(ENOENT);
return;
}
.to_owned();
let link = if let Some(link) = link.to_str() {
link
} else {
// If we can't decode the filename, pretend it doesn't exist.
error!("symlink: Link 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.symlink(parent_vnode, name, link).await? {
DataResponse::Success(metadata) => {
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!("symlink(parent: {:#x?}) failed: {:?}", parent_vnode, message);
reply.error(code as c_int);
}
}
Ok(())
},
"symlink",
);
reply.error(EPERM);
}
/// Rename a file.

View File

@ -222,6 +222,17 @@ impl Requester {
.await
}
pub async fn symlink(
&self,
dir_vnode: VnodeId,
name: String,
link: String
) -> anyhow::Result<DataResponse<FileMetadata>> {
self.internal
.command(&DataCommand::MakeSymlink { dir_vnode, name, link })
.await
}
pub async fn unlink(
&self,
dir_vnode: VnodeId,

View File

@ -103,6 +103,11 @@ pub enum DataCommand {
dir_vnode: VnodeId,
name: String,
},
MakeSymlink {
dir_vnode: VnodeId,
name: String,
link: String,
},
}
#[derive(Serialize, Deserialize, Debug, Clone)]

View File

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

View File

@ -794,4 +794,25 @@ impl FileAccess {
tokio::fs::remove_dir(&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 symlink(
&self,
dir_vnode: VnodeId,
name: String,
link: 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);
let src = Path::new(&link);
tokio::fs::symlink(src, &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))
}
}