vfs: fix error enum

This commit is contained in:
dr-frmr 2024-12-23 02:05:39 -05:00
parent 35e0334703
commit 37373acd44
No known key found for this signature in database
3 changed files with 52 additions and 167 deletions

View File

@ -287,14 +287,12 @@ async fn handle_request(
.. ..
}) = km.message }) = km.message
else { else {
return Err(VfsError::BadRequest { // we got a response -- safe to ignore
error: "not a request".into(), return Ok(());
});
}; };
let request: VfsRequest = serde_json::from_slice(&body).map_err(|e| VfsError::BadJson { let request: VfsRequest =
error: e.to_string(), serde_json::from_slice(&body).map_err(|_| VfsError::MalformedRequest)?;
})?;
// special case for root reading list of all drives. // special case for root reading list of all drives.
if request.action == VfsAction::ReadDir && request.path == "/" { if request.action == VfsAction::ReadDir && request.path == "/" {
@ -336,10 +334,7 @@ async fn handle_request(
.await; .await;
return Ok(()); return Ok(());
} else { } else {
return Err(VfsError::NoCap { return Err(VfsError::NoReadCap);
action: request.action.to_string(),
path: request.path,
});
} }
} }
@ -348,7 +343,6 @@ async fn handle_request(
// must have prepended `/` here or else it messes up caps downstream, e.g. in run-tests // must have prepended `/` here or else it messes up caps downstream, e.g. in run-tests
let drive = format!("/{package_id}/{drive}"); let drive = format!("/{package_id}/{drive}");
let action = request.action; let action = request.action;
let path = PathBuf::from(&request.path);
if km.source.process != *KERNEL_PROCESS_ID { if km.source.process != *KERNEL_PROCESS_ID {
check_caps( check_caps(
@ -356,7 +350,6 @@ async fn handle_request(
&km.source, &km.source,
&send_to_caps_oracle, &send_to_caps_oracle,
&action, &action,
&path,
&drive, &drive,
&package_id, &package_id,
vfs_path, vfs_path,
@ -406,9 +399,7 @@ async fn handle_request(
VfsAction::WriteAll => { VfsAction::WriteAll => {
// doesn't create a file, writes at exact cursor. // doesn't create a file, writes at exact cursor.
let Some(blob) = km.lazy_load_blob else { let Some(blob) = km.lazy_load_blob else {
return Err(VfsError::BadRequest { return Err(VfsError::NoBlob);
error: "blob needs to exist for WriteAll".into(),
});
}; };
let file = files.open_file(&path, false, false).await?; let file = files.open_file(&path, false, false).await?;
let mut file = file.lock().await; let mut file = file.lock().await;
@ -417,18 +408,14 @@ async fn handle_request(
} }
VfsAction::Write => { VfsAction::Write => {
let Some(blob) = km.lazy_load_blob else { let Some(blob) = km.lazy_load_blob else {
return Err(VfsError::BadRequest { return Err(VfsError::NoBlob);
error: "blob needs to exist for Write".into(),
});
}; };
fs::write(&path, &blob.bytes).await?; fs::write(&path, &blob.bytes).await?;
(VfsResponse::Ok, None) (VfsResponse::Ok, None)
} }
VfsAction::Append => { VfsAction::Append => {
let Some(blob) = km.lazy_load_blob else { let Some(blob) = km.lazy_load_blob else {
return Err(VfsError::BadRequest { return Err(VfsError::NoBlob);
error: "blob needs to exist for Append".into(),
});
}; };
let file = files.open_file(&path, false, false).await?; let file = files.open_file(&path, false, false).await?;
let mut file = file.lock().await; let mut file = file.lock().await;
@ -526,29 +513,16 @@ async fn handle_request(
} }
VfsAction::Rename { new_path } => { VfsAction::Rename { new_path } => {
let new_path = join_paths_safely(vfs_path, &new_path); let new_path = join_paths_safely(vfs_path, &new_path);
fs::rename(&path, new_path) fs::rename(&path, new_path).await?;
.await
.map_err(|e| VfsError::IOError {
error: e.to_string(),
path: request.path,
})?;
(VfsResponse::Ok, None) (VfsResponse::Ok, None)
} }
VfsAction::CopyFile { new_path } => { VfsAction::CopyFile { new_path } => {
let new_path = join_paths_safely(vfs_path, &new_path); let new_path = join_paths_safely(vfs_path, &new_path);
fs::copy(&path, new_path) fs::copy(&path, new_path).await?;
.await
.map_err(|e| VfsError::IOError {
error: e.to_string(),
path: request.path,
})?;
(VfsResponse::Ok, None) (VfsResponse::Ok, None)
} }
VfsAction::Metadata => { VfsAction::Metadata => {
let metadata = fs::metadata(&path).await.map_err(|e| VfsError::IOError { let metadata = fs::metadata(&path).await?;
error: e.to_string(),
path: request.path,
})?;
let file_type = get_file_type(&metadata); let file_type = get_file_type(&metadata);
let meta = FileMetadata { let meta = FileMetadata {
len: metadata.len(), len: metadata.len(),
@ -559,23 +533,13 @@ async fn handle_request(
VfsAction::Len => { VfsAction::Len => {
let file = files.open_file(&path, false, false).await?; let file = files.open_file(&path, false, false).await?;
let file = file.lock().await; let file = file.lock().await;
let len = file let len = file.metadata().await?.len();
.metadata()
.await
.map_err(|e| VfsError::IOError {
error: e.to_string(),
path: request.path,
})?
.len();
(VfsResponse::Len(len), None) (VfsResponse::Len(len), None)
} }
VfsAction::SetLen(len) => { VfsAction::SetLen(len) => {
let file = files.open_file(&path, false, false).await?; let file = files.open_file(&path, false, false).await?;
let file = file.lock().await; let file = file.lock().await;
file.set_len(len).await.map_err(|e| VfsError::IOError { file.set_len(len).await?;
error: e.to_string(),
path: request.path,
})?;
(VfsResponse::Ok, None) (VfsResponse::Ok, None)
} }
VfsAction::Hash => { VfsAction::Hash => {
@ -597,9 +561,7 @@ async fn handle_request(
} }
VfsAction::AddZip => { VfsAction::AddZip => {
let Some(blob) = km.lazy_load_blob else { let Some(blob) = km.lazy_load_blob else {
return Err(VfsError::BadRequest { return Err(VfsError::NoBlob);
error: "blob needs to exist for AddZip".into(),
});
}; };
let file = std::io::Cursor::new(&blob.bytes); let file = std::io::Cursor::new(&blob.bytes);
let mut zip = match zip::ZipArchive::new(file) { let mut zip = match zip::ZipArchive::new(file) {
@ -620,10 +582,7 @@ async fn handle_request(
// Before any `.await`s are called since ZipFile is not // Before any `.await`s are called since ZipFile is not
// Send and so does not play nicely with await // Send and so does not play nicely with await
let (is_file, is_dir, local_path, file_contents) = { let (is_file, is_dir, local_path, file_contents) = {
let mut file = zip.by_index(i).map_err(|e| VfsError::IOError { let mut file = zip.by_index(i).map_err(|_| VfsError::UnzipError)?;
error: e.to_string(),
path: request.path.clone(),
})?;
let is_file = file.is_file(); let is_file = file.is_file();
let is_dir = file.is_dir(); let is_dir = file.is_dir();
let mut file_contents = Vec::new(); let mut file_contents = Vec::new();
@ -638,10 +597,7 @@ async fn handle_request(
} else if is_dir { } else if is_dir {
fs::create_dir_all(&local_path).await?; fs::create_dir_all(&local_path).await?;
} else { } else {
return Err(VfsError::CreateDirError { return Err(VfsError::UnzipError);
path: path.display().to_string(),
error: "vfs: zip with non-file non-dir".into(),
});
}; };
} }
(VfsResponse::Ok, None) (VfsResponse::Ok, None)
@ -684,17 +640,13 @@ fn parse_package_and_drive(
// sanitize path.. // sanitize path..
let normalized_path = normalize_path(&joined_path); let normalized_path = normalize_path(&joined_path);
if !normalized_path.starts_with(vfs_path) { if !normalized_path.starts_with(vfs_path) {
return Err(VfsError::BadRequest { return Err(VfsError::MalformedRequest);
error: format!("input path tries to escape parent vfs directory: {path}"),
})?;
} }
// extract original path. // extract original path.
let path = normalized_path let path = normalized_path
.strip_prefix(vfs_path) .strip_prefix(vfs_path)
.map_err(|_| VfsError::BadRequest { .map_err(|_| VfsError::MalformedRequest)?
error: format!("input path tries to escape parent vfs directory: {path}"),
})?
.display() .display()
.to_string(); .to_string();
@ -707,19 +659,13 @@ fn parse_package_and_drive(
parts.remove(0); parts.remove(0);
} }
if parts.len() < 2 { if parts.len() < 2 {
return Err(VfsError::ParseError { return Err(VfsError::MalformedRequest);
error: "malformed path".into(),
path,
});
} }
let package_id = match parts[0].parse::<PackageId>() { let package_id = match parts[0].parse::<PackageId>() {
Ok(id) => id, Ok(id) => id,
Err(e) => { Err(_) => {
return Err(VfsError::ParseError { return Err(VfsError::MalformedRequest);
error: e.to_string(),
path,
})
} }
}; };
@ -774,7 +720,6 @@ async fn check_caps(
source: &Address, source: &Address,
send_to_caps_oracle: &CapMessageSender, send_to_caps_oracle: &CapMessageSender,
action: &VfsAction, action: &VfsAction,
path: &PathBuf,
drive: &str, drive: &str,
package_id: &PackageId, package_id: &PackageId,
vfs_path: &PathBuf, vfs_path: &PathBuf,
@ -809,10 +754,7 @@ async fn check_caps(
if read_capability("", "", true, our_node, source, send_to_caps_oracle).await { if read_capability("", "", true, our_node, source, send_to_caps_oracle).await {
return Ok(()); return Ok(());
} }
return Err(VfsError::NoCap { return Err(VfsError::NoWriteCap);
action: action.to_string(),
path: path.display().to_string(),
});
} }
Ok(()) Ok(())
} }
@ -835,10 +777,7 @@ async fn check_caps(
if read_capability("", "", true, our_node, source, send_to_caps_oracle).await { if read_capability("", "", true, our_node, source, send_to_caps_oracle).await {
return Ok(()); return Ok(());
} }
return Err(VfsError::NoCap { return Err(VfsError::NoReadCap);
action: action.to_string(),
path: path.display().to_string(),
});
} }
Ok(()) Ok(())
} }
@ -867,10 +806,7 @@ async fn check_caps(
if read_capability("", "", true, our_node, source, send_to_caps_oracle).await { if read_capability("", "", true, our_node, source, send_to_caps_oracle).await {
return Ok(()); return Ok(());
} }
return Err(VfsError::NoCap { return Err(VfsError::NoWriteCap);
action: action.to_string(),
path: path.display().to_string(),
});
} }
// if they're within the same drive, no need for 2 caps checks // if they're within the same drive, no need for 2 caps checks
@ -892,10 +828,7 @@ async fn check_caps(
if read_capability("", "", true, our_node, source, send_to_caps_oracle).await { if read_capability("", "", true, our_node, source, send_to_caps_oracle).await {
return Ok(()); return Ok(());
} }
return Err(VfsError::NoCap { return Err(VfsError::NoWriteCap);
action: action.to_string(),
path: path.display().to_string(),
});
} }
Ok(()) Ok(())
} }
@ -903,10 +836,7 @@ async fn check_caps(
if &src_package_id != package_id { if &src_package_id != package_id {
// check for root cap // check for root cap
if !read_capability("", "", true, our_node, source, send_to_caps_oracle).await { if !read_capability("", "", true, our_node, source, send_to_caps_oracle).await {
return Err(VfsError::NoCap { return Err(VfsError::NoWriteCap);
action: action.to_string(),
path: path.display().to_string(),
});
} }
} }
add_capability("read", &drive, &our_node, &source, send_to_caps_oracle).await?; add_capability("read", &drive, &our_node, &source, send_to_caps_oracle).await?;
@ -958,20 +888,20 @@ async fn add_capability(
format!("{{\"kind\": \"{kind}\", \"drive\": \"{drive}\"}}"), format!("{{\"kind\": \"{kind}\", \"drive\": \"{drive}\"}}"),
); );
let (send_cap_bool, recv_cap_bool) = tokio::sync::oneshot::channel(); let (send_cap_bool, recv_cap_bool) = tokio::sync::oneshot::channel();
send_to_caps_oracle let Ok(()) = send_to_caps_oracle
.send(CapMessage::Add { .send(CapMessage::Add {
on: source.process.clone(), on: source.process.clone(),
caps: vec![cap], caps: vec![cap],
responder: Some(send_cap_bool), responder: Some(send_cap_bool),
}) })
.await?; .await
match recv_cap_bool.await? { else {
true => Ok(()), return Err(VfsError::AddCapFailed);
false => Err(VfsError::NoCap { };
action: "add_capability".to_string(), let Ok(true) = recv_cap_bool.await else {
path: drive.to_string(), return Err(VfsError::AddCapFailed);
}), };
} Ok(())
} }
fn get_file_type(metadata: &std::fs::Metadata) -> FileType { fn get_file_type(metadata: &std::fs::Metadata) -> FileType {

View File

@ -2,6 +2,8 @@ use crate::types::core::ProcessId;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use thiserror::Error; use thiserror::Error;
// state:distro:sys is INTERNAL -- this interface is NEVER EXPOSED TO USERSPACE
/// IPC Requests for the state:distro:sys runtime module. /// IPC Requests for the state:distro:sys runtime module.
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub enum StateAction { pub enum StateAction {

View File

@ -1,4 +1,3 @@
use crate::types::core::CapMessage;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use thiserror::Error; use thiserror::Error;
@ -81,72 +80,26 @@ pub enum VfsResponse {
#[derive(Error, Debug, Serialize, Deserialize)] #[derive(Error, Debug, Serialize, Deserialize)]
pub enum VfsError { pub enum VfsError {
#[error("No capability for action {action} at path {path}")] #[error("no write capability for requested drive")]
NoCap { action: String, path: String }, NoWriteCap,
#[error("Bytes blob required for {action} at path {path}")] #[error("no read capability for requested drive")]
BadBytes { action: String, path: String }, NoReadCap,
#[error("bad request error: {error}")] #[error("failed to generate capability for new drive")]
BadRequest { error: String }, AddCapFailed,
#[error("request could not be deserialized to valid VfsRequest")]
MalformedRequest,
#[error("request type used requires a blob")]
NoBlob,
#[error("error parsing path: {path}: {error}")] #[error("error parsing path: {path}: {error}")]
ParseError { error: String, path: String }, ParseError { error: String, path: String },
#[error("IO error: {error}, at path {path}")] #[error("IO error: {0}")]
IOError { error: String, path: String }, IOError(String),
#[error("kernel capability channel error: {error}")] #[error("non-file non-dir in zip")]
CapChannelFail { error: String }, UnzipError,
#[error("Bad JSON blob: {error}")]
BadJson { error: String },
#[error("File not found at path {path}")]
NotFound { path: String },
#[error("Creating directory failed at path: {path}: {error}")]
CreateDirError { path: String, error: String },
#[error("Other error: {error}")]
Other { error: String },
}
impl VfsError {
pub fn kind(&self) -> &str {
match *self {
VfsError::NoCap { .. } => "NoCap",
VfsError::BadBytes { .. } => "BadBytes",
VfsError::BadRequest { .. } => "BadRequest",
VfsError::ParseError { .. } => "ParseError",
VfsError::IOError { .. } => "IOError",
VfsError::CapChannelFail { .. } => "CapChannelFail",
VfsError::BadJson { .. } => "NoJson",
VfsError::NotFound { .. } => "NotFound",
VfsError::CreateDirError { .. } => "CreateDirError",
VfsError::Other { .. } => "Other",
}
}
}
impl From<tokio::sync::oneshot::error::RecvError> for VfsError {
fn from(err: tokio::sync::oneshot::error::RecvError) -> Self {
VfsError::CapChannelFail {
error: err.to_string(),
}
}
}
impl From<tokio::sync::mpsc::error::SendError<CapMessage>> for VfsError {
fn from(err: tokio::sync::mpsc::error::SendError<CapMessage>) -> Self {
VfsError::CapChannelFail {
error: err.to_string(),
}
}
} }
impl From<std::io::Error> for VfsError { impl From<std::io::Error> for VfsError {
fn from(err: std::io::Error) -> Self { fn from(err: std::io::Error) -> Self {
VfsError::IOError { VfsError::IOError(err.to_string())
path: "".into(),
error: err.to_string(),
}
}
}
impl std::fmt::Display for VfsAction {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
} }
} }