Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ nix = { version = "0.30.1", features = ["mman"], optional = true }
async-channel = { version = "2.5.0", default-features = false }
async-lock = { version = "3.4.1", default-features = false }
serde = { version = "1.0.228", default-features = false, features = ["alloc", "derive"] }
embedded-io = { version = "0.6", default-features = false }

[target.'cfg(target_os="linux")'.dev-dependencies]
rand = "0.9.2"
54 changes: 54 additions & 0 deletions common/src/protocol/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ use alloc::vec::Vec;

use serde::{Deserialize, Serialize};

pub use embedded_io::ErrorKind;

#[derive(Debug, Serialize, Deserialize)]
pub enum Request {
GetArgs,
Exit(i32),
Open(String, FileMode),
Mkdir(String),
Listen { ip: [u8; 4], port: u16 },
Connect { host: String, port: u16 },
}
Expand All @@ -17,6 +20,57 @@ pub enum Request {
pub enum Response {
Args(Vec<String>),
Pipe(PipeData),
Ack,
Err(IoErrorKind),
}

/// Wire-serializable mirror of [`embedded_io::ErrorKind`], which is
/// `#[non_exhaustive]` and not `serde`-derivable. Carries a host I/O failure
/// back to the guest, where it converts into an `embedded_io::ErrorKind`.
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub enum IoErrorKind {
NotFound,
PermissionDenied,
AlreadyExists,
InvalidInput,
InvalidData,
Unsupported,
OutOfMemory,
Interrupted,
Other,
}

impl From<IoErrorKind> for ErrorKind {
fn from(e: IoErrorKind) -> Self {
match e {
IoErrorKind::NotFound => ErrorKind::NotFound,
IoErrorKind::PermissionDenied => ErrorKind::PermissionDenied,
IoErrorKind::AlreadyExists => ErrorKind::AlreadyExists,
IoErrorKind::InvalidInput => ErrorKind::InvalidInput,
IoErrorKind::InvalidData => ErrorKind::InvalidData,
IoErrorKind::Unsupported => ErrorKind::Unsupported,
IoErrorKind::OutOfMemory => ErrorKind::OutOfMemory,
IoErrorKind::Interrupted => ErrorKind::Interrupted,
IoErrorKind::Other => ErrorKind::Other,
}
}
}

#[cfg(feature = "std")]
impl From<std::io::ErrorKind> for IoErrorKind {
fn from(e: std::io::ErrorKind) -> Self {
match e {
std::io::ErrorKind::NotFound => IoErrorKind::NotFound,
std::io::ErrorKind::PermissionDenied => IoErrorKind::PermissionDenied,
std::io::ErrorKind::AlreadyExists => IoErrorKind::AlreadyExists,
std::io::ErrorKind::InvalidInput => IoErrorKind::InvalidInput,
std::io::ErrorKind::InvalidData => IoErrorKind::InvalidData,
std::io::ErrorKind::Unsupported => IoErrorKind::Unsupported,
std::io::ErrorKind::OutOfMemory => IoErrorKind::OutOfMemory,
std::io::ErrorKind::Interrupted => IoErrorKind::Interrupted,
_ => IoErrorKind::Other,
}
}
}

#[derive(Debug, Serialize, Deserialize)]
Expand Down
2 changes: 1 addition & 1 deletion fix/.cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ rustflags = [
"-C", "relocation-model=static",
# "-C", "target-feature=+crt-static",
]
runner = "cargo run -p evaluator --bin fix-cli -- hybrid"
runner = "cargo run -p vmm --"
34 changes: 30 additions & 4 deletions fix/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![no_main]
#![no_std]
use kernel::host::fs::{File, Whence};
use kernel::host::fs::{self, File, Whence};
use kernel::host::os;
use kernel::prelude::*;

Expand All @@ -16,8 +16,36 @@ use derive_more::Unwrap;
#[kmain]
fn main() {
let argv = os::argv();
let filename = argv.get(1).expect("expected command file");

// Subcommand dispatch: `fix init` | `fix eval <file>`.
match argv.get(1).map(String::as_str) {
Some("init") => init(),
Some("eval") => {
let filename = argv.get(2).expect("fix eval: expected a command file");
eval_file(filename);
}
Some(other) => panic!("fix: unknown command '{other}' (expected: init | eval <file>)"),
None => panic!("fix: expected a command (init | eval <file>)"),
}

kernel::shutdown();
}

/// `fix init`: create the on-disk `.fix` store with its `objects/` and
/// `labels/` subdirs. `mkdir` maps to host `create_dir_all`, so re-running on an
/// existing store is harmless (matches git's "reinitialized existing repository").
fn init() {
for dir in [".fix/objects", ".fix/labels"] {
if let Err(e) = fs::mkdir(dir) {
println!("fix init: failed to create {dir}: {e:?}");
kernel::exit(1);
}
}
println!("initialized empty fix store in .fix");
}

/// `fix eval <file>`: read, parse, and evaluate a command file.
fn eval_file(filename: &str) {
let mut file = File::open(filename, true, false, false, false, false).unwrap();
let len = file.seek(Whence::End(0)) as usize;
file.seek(Whence::Start(0));
Expand Down Expand Up @@ -73,8 +101,6 @@ fn main() {
}
}
}

kernel::shutdown();
}

#[derive(Clone, Debug, Unwrap)]
Expand Down
37 changes: 27 additions & 10 deletions kernel/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,28 @@ pub mod fs {
use super::get_pipe;
use crate::pipe::*;
pub use common::protocol::file::Whence;
use common::{protocol::control::FileMode, protocol::*};
use common::{
protocol::control::{ErrorKind, FileMode},
protocol::*,
};

pub struct File {
pipe: FilePipe,
}

/// Recursively creates a directory and all of its parents on the host, like
/// `mkdir -p`. Mirrors [`File::open`], but the host has nothing to stream
/// back, so success is a bare ack and failure carries an [`ErrorKind`].
pub fn mkdir(path: &str) -> Result<(), ErrorKind> {
let mut binding = crate::pipe::HOST.lock();
let host = binding.get_mut().unwrap();
match host.request(&control::Request::Mkdir(path.into())) {
control::Response::Ack => Ok(()),
control::Response::Err(e) => Err(e.into()),
_ => Err(ErrorKind::Other),
}
}

impl File {
pub fn open(
path: &str,
Expand All @@ -252,10 +268,10 @@ pub mod fs {
create: bool,
append: bool,
truncate: bool,
) -> Option<File> {
) -> Result<File, ErrorKind> {
let mut binding = crate::pipe::HOST.lock();
let host = binding.get_mut().unwrap();
let control::Response::Pipe(id) = host.request(&control::Request::Open(
match host.request(&control::Request::Open(
path.into(),
FileMode {
read,
Expand All @@ -264,13 +280,14 @@ pub mod fs {
append,
truncate,
},
)) else {
return None;
};
unsafe {
Some(File {
pipe: FilePipe::new(get_pipe(id)),
})
)) {
control::Response::Pipe(id) => unsafe {
Ok(File {
pipe: FilePipe::new(get_pipe(id)),
})
},
control::Response::Err(e) => Err(e.into()),
_ => Err(ErrorKind::Other),
}
}

Expand Down
6 changes: 5 additions & 1 deletion vmm/src/comm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,13 @@ pub fn control_thread(argv: Vec<String>, mut pipe: ControlPipe) {
});
Response::Pipe(decompose_pipe(p))
}
Err(x) => todo!("open: {x:?}"),
Err(e) => Response::Err(e.kind().into()),
}
}
Request::Mkdir(path) => match std::fs::create_dir_all(&path) {
Ok(()) => Response::Ack,
Err(e) => Response::Err(e.kind().into()),
},
Request::Listen { ip, port } => {
let listener = TcpListener::bind(SocketAddr::from((ip, port))).unwrap();
let (p, q) = common::pipe::pipe(1024);
Expand Down
Loading