From dd3bdb56dd274d5279861453c0e60852b7152617 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Wed, 3 Jan 2024 22:23:49 +1100 Subject: [PATCH 01/67] First implementation of DProxy --- Cargo.lock | 18 + lib/cli/Cargo.toml | 1 + lib/cli/src/commands/run/mod.rs | 15 + lib/virtual-io/src/arc.rs | 43 ++ lib/virtual-io/src/lib.rs | 2 + lib/virtual-net/Cargo.toml | 5 + lib/virtual-net/src/composite.rs | 121 ++++ lib/virtual-net/src/lib.rs | 16 +- lib/virtual-net/src/loopback.rs | 240 +++++++ lib/virtual-net/src/meta.rs | 20 +- lib/virtual-net/src/tcp_pair.rs | 633 ++++++++++++++++++ lib/wasix/Cargo.toml | 2 + lib/wasix/src/journal/concrete/archived.rs | 5 +- .../effector/syscalls/port_addr_remove.rs | 2 +- .../effector/syscalls/port_gateway_set.rs | 2 +- .../effector/syscalls/port_route_add.rs | 4 +- .../effector/syscalls/port_route_remove.rs | 2 +- .../journal/effector/syscalls/sock_accept.rs | 2 +- .../journal/effector/syscalls/sock_bind.rs | 2 +- .../journal/effector/syscalls/sock_connect.rs | 2 +- .../syscalls/sock_join_ipv4_multicast.rs | 2 +- .../syscalls/sock_join_ipv6_multicast.rs | 2 +- .../syscalls/sock_leave_ipv4_multicast.rs | 2 +- .../syscalls/sock_leave_ipv6_multicast.rs | 2 +- .../journal/effector/syscalls/sock_send_to.rs | 2 +- .../effector/syscalls/sock_set_opt_time.rs | 2 +- lib/wasix/src/journal/entry.rs | 5 +- lib/wasix/src/runners/dproxy/factory.rs | 102 +++ lib/wasix/src/runners/dproxy/handler.rs | 121 ++++ .../src/runners/dproxy/hyper_proxy/builder.rs | 32 + .../runners/dproxy/hyper_proxy/connector.rs | 39 ++ .../src/runners/dproxy/hyper_proxy/mod.rs | 18 + .../src/runners/dproxy/hyper_proxy/stream.rs | 53 ++ lib/wasix/src/runners/dproxy/instance.rs | 13 + lib/wasix/src/runners/dproxy/mod.rs | 10 + lib/wasix/src/runners/dproxy/networking.rs | 258 +++++++ lib/wasix/src/runners/dproxy/runner.rs | 147 ++++ lib/wasix/src/runners/dproxy/shard.rs | 6 + .../src/runners/dproxy/socket_manager.rs | 164 +++++ lib/wasix/src/runners/mod.rs | 2 + 40 files changed, 2087 insertions(+), 32 deletions(-) create mode 100644 lib/virtual-io/src/arc.rs create mode 100644 lib/virtual-net/src/composite.rs create mode 100644 lib/virtual-net/src/loopback.rs create mode 100644 lib/virtual-net/src/tcp_pair.rs create mode 100644 lib/wasix/src/runners/dproxy/factory.rs create mode 100644 lib/wasix/src/runners/dproxy/handler.rs create mode 100644 lib/wasix/src/runners/dproxy/hyper_proxy/builder.rs create mode 100644 lib/wasix/src/runners/dproxy/hyper_proxy/connector.rs create mode 100644 lib/wasix/src/runners/dproxy/hyper_proxy/mod.rs create mode 100644 lib/wasix/src/runners/dproxy/hyper_proxy/stream.rs create mode 100644 lib/wasix/src/runners/dproxy/instance.rs create mode 100644 lib/wasix/src/runners/dproxy/mod.rs create mode 100644 lib/wasix/src/runners/dproxy/networking.rs create mode 100644 lib/wasix/src/runners/dproxy/runner.rs create mode 100644 lib/wasix/src/runners/dproxy/shard.rs create mode 100644 lib/wasix/src/runners/dproxy/socket_manager.rs diff --git a/Cargo.lock b/Cargo.lock index 9069409501b..72a2ca3bea4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2582,6 +2582,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "managed" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" + [[package]] name = "maplit" version = "1.0.2" @@ -4281,6 +4287,17 @@ version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +[[package]] +name = "smoltcp" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee34c1e1bfc7e9206cc0fb8030a90129b4e319ab53856249bb27642cab914fb3" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "managed", +] + [[package]] name = "socket2" version = "0.4.10" @@ -5319,6 +5336,7 @@ dependencies = [ "rkyv", "serde", "serial_test 2.0.0", + "smoltcp", "socket2 0.4.10", "thiserror", "tokio", diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index 5decba23f30..b5218b35fec 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -40,6 +40,7 @@ wasmer-wasix = { version = "0.18.0", path = "../wasix", features = [ "logging", "webc_runner_rt_wcgi", "webc_runner_rt_dcgi", + "webc_runner_rt_dproxy", "webc_runner_rt_emscripten", "host-fs", ] } diff --git a/lib/cli/src/commands/run/mod.rs b/lib/cli/src/commands/run/mod.rs index 1f430493635..aedffec0c23 100644 --- a/lib/cli/src/commands/run/mod.rs +++ b/lib/cli/src/commands/run/mod.rs @@ -35,6 +35,7 @@ use wasmer_wasix::{ journal::CompactingLogFileJournal, runners::{ dcgi::DcgiInstanceFactory, + dproxy::DProxyRunner, wcgi::{self, NoOpWcgiCallbacks}, MappedCommand, MappedDirectory, Runner, }, @@ -191,6 +192,8 @@ impl Run { if DcgiRunner::can_run_command(cmd.metadata())? { self.run_dcgi(id, pkg, uses, runtime) + } else if DProxyRunner::can_run_command(cmd.metadata())? { + self.run_dproxy(id, pkg, uses, runtime) } else if WcgiRunner::can_run_command(cmd.metadata())? { self.run_wcgi(id, pkg, uses, runtime) } else if WasiRunner::can_run_command(cmd.metadata())? { @@ -308,6 +311,18 @@ impl Run { runner.run_command(command_name, pkg, runtime) } + fn run_dproxy( + &self, + command_name: &str, + pkg: &BinaryPackage, + uses: Vec, + runtime: Arc, + ) -> Result<(), Error> { + let mut inner = self.build_wasi_runner(&runtime)?; + let mut runner = wasmer_wasix::runners::dproxy::DProxyRunner::new(inner, uses); + runner.run(command_name, pkg, runtime) + } + fn run_emscripten( &self, command_name: &str, diff --git a/lib/virtual-io/src/arc.rs b/lib/virtual-io/src/arc.rs new file mode 100644 index 00000000000..7abc45f58c0 --- /dev/null +++ b/lib/virtual-io/src/arc.rs @@ -0,0 +1,43 @@ +use std::sync::Arc; +use std::sync::Mutex; + +use derivative::Derivative; + +use crate::{InterestHandler, InterestType}; + +#[derive(Derivative)] +#[derivative(Debug)] +struct ArcInterestHandlerState { + #[derivative(Debug = "ignore")] + handler: Box, +} + +#[derive(Debug, Clone)] +pub struct ArcInterestHandler { + state: Arc>, +} + +impl ArcInterestHandler { + pub fn new(handler: Box) -> Self { + Self { + state: Arc::new(Mutex::new(ArcInterestHandlerState { handler })), + } + } +} + +impl InterestHandler for ArcInterestHandler { + fn push_interest(&mut self, interest: InterestType) { + let mut state = self.state.lock().unwrap(); + state.handler.push_interest(interest) + } + + fn pop_interest(&mut self, interest: InterestType) -> bool { + let mut state = self.state.lock().unwrap(); + state.handler.pop_interest(interest) + } + + fn has_interest(&self, interest: InterestType) -> bool { + let state = self.state.lock().unwrap(); + state.handler.has_interest(interest) + } +} diff --git a/lib/virtual-io/src/lib.rs b/lib/virtual-io/src/lib.rs index f5fa03bbc8e..e2e52b9a6f6 100644 --- a/lib/virtual-io/src/lib.rs +++ b/lib/virtual-io/src/lib.rs @@ -1,3 +1,4 @@ +mod arc; #[cfg(feature = "sys")] mod guard; mod interest; @@ -5,6 +6,7 @@ mod interest; mod selector; pub mod waker; +pub use arc::*; #[cfg(feature = "sys")] pub use guard::*; pub use interest::*; diff --git a/lib/virtual-net/Cargo.toml b/lib/virtual-net/Cargo.toml index db23e664d3d..654086be6ec 100644 --- a/lib/virtual-net/Cargo.toml +++ b/lib/virtual-net/Cargo.toml @@ -34,6 +34,11 @@ tokio-tungstenite = { version = "0.20", optional = true } rkyv = { version = "0.7.40", features = ["indexmap", "validation", "strict"], optional = true } bytecheck = { version = "0.6.8", optional = true } +[dependencies.smoltcp] +version = "0.8" +default-features = false +features = ["proto-ipv4", "std", "alloc"] + [dev-dependencies] tokio = { version = "1", default_features = false, features = [ "macros", "rt-multi-thread" ] } tracing-test = { version = "0.2" } diff --git a/lib/virtual-net/src/composite.rs b/lib/virtual-net/src/composite.rs new file mode 100644 index 00000000000..6901bca32b7 --- /dev/null +++ b/lib/virtual-net/src/composite.rs @@ -0,0 +1,121 @@ +use std::net::SocketAddr; +use std::task::{Context, Poll}; + +use crate::{Ipv4Addr, Ipv6Addr, NetworkError, VirtualIoSource, VirtualTcpListener}; +use derivative::Derivative; +use virtual_mio::ArcInterestHandler; + +#[derive(Derivative)] +#[derivative(Debug)] +pub struct CompositeTcpListener { + #[derivative(Debug = "ignore")] + ports: Vec>, +} + +impl CompositeTcpListener { + pub fn new() -> Self { + Self { ports: Vec::new() } + } + + pub fn add_port(&mut self, port: Box) { + self.ports.push(port); + } +} + +impl Default for CompositeTcpListener { + fn default() -> Self { + Self::new() + } +} + +impl VirtualIoSource for CompositeTcpListener { + fn remove_handler(&mut self) { + for port in self.ports.iter_mut() { + port.remove_handler(); + } + } + + fn poll_read_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + for port in self.ports.iter_mut() { + if let Poll::Ready(ready) = port.poll_read_ready(cx) { + return Poll::Ready(ready); + } + } + Poll::Pending + } + + fn poll_write_ready( + &mut self, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + for port in self.ports.iter_mut() { + if let Poll::Ready(ready) = port.poll_write_ready(cx) { + return Poll::Ready(ready); + } + } + Poll::Pending + } +} + +impl VirtualTcpListener for CompositeTcpListener { + fn try_accept( + &mut self, + ) -> crate::Result<(Box, SocketAddr)> { + let mut ret = NetworkError::Unsupported; + for port in self.ports.iter_mut() { + match port.try_accept() { + Ok(ret) => return Ok(ret), + Err(err) => { + ret = err; + } + } + } + Err(ret) + } + + fn set_handler( + &mut self, + handler: Box, + ) -> crate::Result<()> { + let handler = ArcInterestHandler::new(handler); + for port in self.ports.iter_mut() { + port.set_handler(Box::new(handler.clone()))?; + } + Ok(()) + } + + fn addr_local(&self) -> crate::Result { + if self.ports.len() > 1 { + let addr = self.ports.first().unwrap().addr_local()?; + if addr.is_ipv4() { + Ok(SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), addr.port())) + } else { + Ok(SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), addr.port())) + } + } else if let Some(addr) = self.ports.first() { + addr.addr_local() + } else { + Err(NetworkError::Unsupported) + } + } + + fn set_ttl(&mut self, ttl: u8) -> crate::Result<()> { + for port in self.ports.iter_mut() { + match port.set_ttl(ttl) { + Ok(()) | Err(NetworkError::Unsupported) => {} + Err(err) => return Err(err), + } + } + Ok(()) + } + + fn ttl(&self) -> crate::Result { + let mut ret = u8::MAX; + for port in self.ports.iter() { + if let Ok(ttl) = port.ttl() { + ret = ret.min(ttl) + } + } + Ok(ret) + } +} diff --git a/lib/virtual-net/src/lib.rs b/lib/virtual-net/src/lib.rs index c59f613f083..c1930cb2d1e 100644 --- a/lib/virtual-net/src/lib.rs +++ b/lib/virtual-net/src/lib.rs @@ -2,19 +2,24 @@ #[cfg(any(feature = "remote"))] pub mod client; +pub mod composite; #[cfg(feature = "host-net")] pub mod host; +pub mod loopback; pub mod meta; #[cfg(any(feature = "remote"))] pub mod rx_tx; #[cfg(any(feature = "remote"))] pub mod server; +pub mod tcp_pair; #[cfg(feature = "tokio")] #[cfg(test)] mod tests; #[cfg(any(feature = "remote"))] pub use client::{RemoteNetworkingClient, RemoteNetworkingClientDriver}; +pub use composite::CompositeTcpListener; +pub use loopback::LoopbackNetworking; use pin_project_lite::pin_project; #[cfg(feature = "rkyv")] use rkyv::{Archive, CheckBytes, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; @@ -22,16 +27,17 @@ use rkyv::{Archive, CheckBytes, Deserialize as RkyvDeserialize, Serialize as Rky pub use server::{RemoteNetworkingServer, RemoteNetworkingServerDriver}; use std::fmt; use std::mem::MaybeUninit; -pub use std::net::IpAddr; -pub use std::net::Ipv4Addr; -pub use std::net::Ipv6Addr; +pub(crate) use std::net::IpAddr; +pub(crate) use std::net::Ipv4Addr; +pub(crate) use std::net::Ipv6Addr; use std::net::Shutdown; -pub use std::net::SocketAddr; +pub(crate) use std::net::SocketAddr; use std::pin::Pin; use std::sync::Arc; use std::task::Context; use std::task::Poll; -pub use std::time::Duration; +pub(crate) use std::time::Duration; +pub use tcp_pair::{TcpSocketHalf, TcpSocketHalfRx, TcpSocketHalfTx}; use thiserror::Error; #[cfg(feature = "tokio")] use tokio::io::AsyncRead; diff --git a/lib/virtual-net/src/loopback.rs b/lib/virtual-net/src/loopback.rs new file mode 100644 index 00000000000..7957058ba89 --- /dev/null +++ b/lib/virtual-net/src/loopback.rs @@ -0,0 +1,240 @@ +use std::collections::VecDeque; +use std::net::SocketAddr; +use std::sync::Mutex; +use std::task::{Context, Poll, Waker}; +use std::{collections::HashMap, sync::Arc}; + +use crate::{ + InterestHandler, InterestType, IpAddr, IpCidr, Ipv4Addr, Ipv6Addr, NetworkError, + VirtualIoSource, VirtualNetworking, VirtualTcpListener, VirtualTcpSocket, +}; +use derivative::Derivative; + +use super::TcpSocketHalf; + +const DEFAULT_MAX_BUFFER_SIZE: usize = 1_048_576; + +#[derive(Debug, Default)] +struct LoopbackNetworkingState { + tcp_listeners: HashMap, + ip_addresses: Vec, +} + +#[derive(Debug, Clone)] +pub struct LoopbackNetworking { + state: Arc>, +} + +impl LoopbackNetworking { + pub fn new() -> Self { + LoopbackNetworking { + state: Arc::new(Mutex::new(Default::default())), + } + } + + pub fn loopback_connect_to( + &self, + mut local_addr: SocketAddr, + peer_addr: SocketAddr, + ) -> Option { + let mut port = local_addr.port(); + if port == 0 { + port = peer_addr.port(); + } + + local_addr = match local_addr.ip() { + IpAddr::V4(Ipv4Addr::UNSPECIFIED) => { + SocketAddr::new(Ipv4Addr::new(127, 0, 0, 100).into(), port) + } + IpAddr::V6(Ipv6Addr::UNSPECIFIED) => { + SocketAddr::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 100).into(), port) + } + ip => SocketAddr::new(ip, port), + }; + + let state = self.state.lock().unwrap(); + if let Some(listener) = state.tcp_listeners.get(&peer_addr) { + Some(listener.connect_to(local_addr)) + } else { + state + .tcp_listeners + .iter() + .next() + .map(|listener| listener.1.connect_to(local_addr)) + } + } +} + +impl Default for LoopbackNetworking { + fn default() -> Self { + Self::new() + } +} + +#[allow(unused_variables)] +#[async_trait::async_trait] +impl VirtualNetworking for LoopbackNetworking { + async fn dhcp_acquire(&self) -> crate::Result> { + let mut state: std::sync::MutexGuard<'_, LoopbackNetworkingState> = + self.state.lock().unwrap(); + state.ip_addresses.clear(); + state.ip_addresses.push(IpCidr { + ip: IpAddr::V4(Ipv4Addr::LOCALHOST), + prefix: 32, + }); + state.ip_addresses.push(IpCidr { + ip: IpAddr::V6(Ipv6Addr::LOCALHOST), + prefix: 128, + }); + Ok(state.ip_addresses.iter().map(|cidr| cidr.ip).collect()) + } + + async fn ip_add(&self, ip: IpAddr, prefix: u8) -> crate::Result<()> { + let mut state = self.state.lock().unwrap(); + state.ip_addresses.push(IpCidr { ip, prefix }); + Ok(()) + } + + async fn ip_remove(&self, ip: IpAddr) -> crate::Result<()> { + let mut state: std::sync::MutexGuard<'_, LoopbackNetworkingState> = + self.state.lock().unwrap(); + state.ip_addresses.retain(|cidr| cidr.ip != ip); + Ok(()) + } + + async fn ip_clear(&self) -> crate::Result<()> { + let mut state: std::sync::MutexGuard<'_, LoopbackNetworkingState> = + self.state.lock().unwrap(); + state.ip_addresses.clear(); + Ok(()) + } + + async fn ip_list(&self) -> crate::Result> { + let state: std::sync::MutexGuard<'_, LoopbackNetworkingState> = self.state.lock().unwrap(); + Ok(state.ip_addresses.clone()) + } + + async fn listen_tcp( + &self, + mut addr: SocketAddr, + _only_v6: bool, + _reuse_port: bool, + _reuse_addr: bool, + ) -> crate::Result> { + let listener = LoopbackTcpListener::new(addr); + + if addr.ip() == IpAddr::V4(Ipv4Addr::UNSPECIFIED) { + addr = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), addr.port()); + } else if addr.ip() == IpAddr::V6(Ipv6Addr::UNSPECIFIED) { + addr = SocketAddr::new(Ipv6Addr::LOCALHOST.into(), addr.port()); + } + + let mut state = self.state.lock().unwrap(); + state.tcp_listeners.insert(addr, listener.clone()); + + Ok(Box::new(listener)) + } +} + +#[derive(Derivative)] +#[derivative(Debug)] +struct LoopbackTcpListenerState { + #[derivative(Debug = "ignore")] + handler: Option>, + addr_local: SocketAddr, + backlog: VecDeque, + wakers: Vec, +} + +#[derive(Debug, Clone)] +pub struct LoopbackTcpListener { + state: Arc>, +} + +impl LoopbackTcpListener { + pub fn new(addr_local: SocketAddr) -> Self { + Self { + state: Arc::new(Mutex::new(LoopbackTcpListenerState { + handler: None, + addr_local, + backlog: Default::default(), + wakers: Default::default(), + })), + } + } + + pub fn connect_to(&self, addr_local: SocketAddr) -> TcpSocketHalf { + let mut state = self.state.lock().unwrap(); + let (half1, half2) = + TcpSocketHalf::channel(DEFAULT_MAX_BUFFER_SIZE, state.addr_local, addr_local); + + state.backlog.push_back(half1); + if let Some(handler) = state.handler.as_mut() { + handler.push_interest(InterestType::Readable); + } + state.wakers.drain(..).for_each(|w| w.wake()); + + half2 + } +} + +impl VirtualIoSource for LoopbackTcpListener { + fn remove_handler(&mut self) { + let mut state = self.state.lock().unwrap(); + state.handler.take(); + } + + fn poll_read_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + let mut state = self.state.lock().unwrap(); + if !state.backlog.is_empty() { + return Poll::Ready(Ok(state.backlog.len())); + } + if state.wakers.iter().any(|w| w.will_wake(cx.waker())) == false { + state.wakers.push(cx.waker().clone()); + } + Poll::Pending + } + + fn poll_write_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Pending + } +} + +impl VirtualTcpListener for LoopbackTcpListener { + fn try_accept( + &mut self, + ) -> crate::Result<(Box, SocketAddr)> { + let mut state = self.state.lock().unwrap(); + let next = state.backlog.pop_front(); + if let Some(next) = next { + let peer = next.addr_peer()?; + return Ok((Box::new(next), peer)); + } + Err(NetworkError::WouldBlock) + } + + fn set_handler( + &mut self, + mut handler: Box, + ) -> crate::Result<()> { + let mut state = self.state.lock().unwrap(); + if !state.backlog.is_empty() { + handler.push_interest(InterestType::Readable); + } + state.handler.replace(handler); + Ok(()) + } + + fn addr_local(&self) -> crate::Result { + let state = self.state.lock().unwrap(); + Ok(state.addr_local) + } + + fn set_ttl(&mut self, _ttl: u8) -> crate::Result<()> { + Ok(()) + } + + fn ttl(&self) -> crate::Result { + Ok(64) + } +} diff --git a/lib/virtual-net/src/meta.rs b/lib/virtual-net/src/meta.rs index 8744f0af9ca..16c52daff62 100644 --- a/lib/virtual-net/src/meta.rs +++ b/lib/virtual-net/src/meta.rs @@ -1,15 +1,15 @@ use serde::{Deserialize, Serialize}; -pub use super::Duration; -pub use super::IpAddr; -pub use super::IpCidr; -pub use super::IpRoute; -pub use super::Ipv4Addr; -pub use super::Ipv6Addr; -pub use super::NetworkError; -pub use super::SocketAddr; -pub use super::SocketStatus; -pub use super::StreamSecurity; +pub(crate) use super::Duration; +pub(crate) use super::IpAddr; +pub(crate) use super::IpCidr; +pub(crate) use super::IpRoute; +pub(crate) use super::Ipv4Addr; +pub(crate) use super::Ipv6Addr; +pub(crate) use super::NetworkError; +pub(crate) use super::SocketAddr; +pub(crate) use super::SocketStatus; +pub(crate) use super::StreamSecurity; /// Represents a socket ID #[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/lib/virtual-net/src/tcp_pair.rs b/lib/virtual-net/src/tcp_pair.rs new file mode 100644 index 00000000000..f60652d293e --- /dev/null +++ b/lib/virtual-net/src/tcp_pair.rs @@ -0,0 +1,633 @@ +use crate::{ + net_error_into_io_err, InterestHandler, InterestType, NetworkError, SocketStatus, + VirtualConnectedSocket, VirtualIoSource, VirtualSocket, VirtualTcpSocket, +}; +use bytes::{Buf, Bytes}; +use futures_util::Future; +use smoltcp::storage::RingBuffer; +use std::io::{self}; +use std::pin::Pin; +use std::sync::Arc; +use std::sync::Mutex; +use std::task::{Context, Waker}; +use std::time::Duration; +use std::{net::SocketAddr, task::Poll}; +use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite, BufReader}; +use virtual_mio::ArcInterestHandler; + +#[derive(Debug)] +struct SocketBufferState { + buffer: RingBuffer<'static, u8>, + push_handler: Option, + pull_handler: Option, + wakers: Vec, + dead: bool, + // This flag prevents a poll write ready storm + halt_immediate_poll_write: bool, +} + +impl SocketBufferState { + fn add_waker(&mut self, waker: &Waker) { + if self.wakers.iter().any(|w| w.will_wake(waker)) == false { + self.wakers.push(waker.clone()); + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct SocketBuffer { + state: Arc>, + dead_on_drop: bool, +} + +impl Drop for SocketBuffer { + fn drop(&mut self) { + if self.dead_on_drop { + self.set_dead(); + } + } +} + +impl SocketBuffer { + fn new(max_size: usize) -> Self { + Self { + state: Arc::new(Mutex::new(SocketBufferState { + buffer: RingBuffer::new(vec![0; max_size]), + push_handler: None, + pull_handler: None, + wakers: Vec::new(), + dead: false, + halt_immediate_poll_write: false, + })), + dead_on_drop: false, + } + } + + pub fn set_push_handler(&self, mut handler: ArcInterestHandler) { + let mut state = self.state.lock().unwrap(); + if state.dead { + handler.push_interest(InterestType::Closed); + } + if !state.buffer.is_empty() { + handler.push_interest(InterestType::Readable); + } + state.push_handler.replace(handler); + } + + pub fn set_pull_handler(&self, mut handler: ArcInterestHandler) { + let mut state = self.state.lock().unwrap(); + if state.dead { + handler.push_interest(InterestType::Closed); + } + if !state.buffer.is_full() && state.pull_handler.is_none() { + handler.push_interest(InterestType::Writable); + } + state.pull_handler.replace(handler); + } + + pub fn clear_push_handler(&self) { + let mut state = self.state.lock().unwrap(); + state.push_handler.take(); + } + + pub fn clear_pull_handler(&self) { + let mut state = self.state.lock().unwrap(); + state.pull_handler.take(); + } + + pub fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { + let mut state = self.state.lock().unwrap(); + if !state.buffer.is_empty() { + return Poll::Ready(Ok(state.buffer.len())); + } + if state.dead { + return Poll::Ready(Ok(0)); + } + if state.wakers.iter().any(|w| w.will_wake(cx.waker())) == false { + state.wakers.push(cx.waker().clone()); + } + Poll::Pending + } + + pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { + let mut state = self.state.lock().unwrap(); + if state.dead { + return Poll::Ready(Ok(0)); + } + if !state.buffer.is_full() && state.halt_immediate_poll_write == false { + state.halt_immediate_poll_write = true; + return Poll::Ready(Ok(state.buffer.window())); + } + if state.wakers.iter().any(|w| w.will_wake(cx.waker())) == false { + state.wakers.push(cx.waker().clone()); + } + Poll::Pending + } + + pub fn set_dead(&self) { + let mut state = self.state.lock().unwrap(); + state.dead = true; + if let Some(handler) = state.pull_handler.as_mut() { + handler.push_interest(InterestType::Closed); + } + if let Some(handler) = state.push_handler.as_mut() { + handler.push_interest(InterestType::Closed); + } + state.wakers.drain(..).for_each(|w| w.wake()); + } + + pub fn is_dead(&self) -> bool { + let state = self.state.lock().unwrap(); + state.dead + } + + pub fn try_send( + &self, + data: &[u8], + all_or_nothing: bool, + waker: Option<&Waker>, + ) -> crate::Result { + let mut state = self.state.lock().unwrap(); + if state.dead { + return Err(NetworkError::ConnectionReset); + } + state.halt_immediate_poll_write = false; + let available = state.buffer.window(); + if available == 0 { + if let Some(waker) = waker { + state.add_waker(waker) + } + return Err(NetworkError::WouldBlock); + } + if data.len() > available { + if all_or_nothing { + if let Some(waker) = waker { + state.add_waker(waker) + } + return Err(NetworkError::WouldBlock); + } + let amt = state.buffer.enqueue_slice(&data[..available]); + return Ok(amt); + } + let amt = state.buffer.enqueue_slice(data); + + if let Some(handler) = state.push_handler.as_mut() { + handler.push_interest(InterestType::Readable); + } + state.wakers.drain(..).for_each(|w| w.wake()); + Ok(amt) + } + + pub async fn send(&self, data: Bytes) -> crate::Result<()> { + struct Poller<'a> { + this: &'a SocketBuffer, + data: Bytes, + } + impl<'a> Future for Poller<'a> { + type Output = crate::Result<()>; + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + loop { + if self.data.is_empty() { + return Poll::Ready(Ok(())); + } + return match self.this.try_send(&self.data, false, Some(cx.waker())) { + Ok(amt) => { + self.data.advance(amt); + continue; + } + Err(NetworkError::WouldBlock) => Poll::Pending, + Err(err) => Poll::Ready(Err(err)), + }; + } + } + } + Poller { this: self, data }.await + } + + pub fn try_read( + &self, + buf: &mut [std::mem::MaybeUninit], + waker: Option<&Waker>, + ) -> crate::Result { + let mut state = self.state.lock().unwrap(); + if state.buffer.is_empty() { + if state.dead { + return Err(NetworkError::ConnectionReset); + } + + if let Some(waker) = waker { + state.add_waker(waker) + } + return Err(NetworkError::WouldBlock); + } + + let buf: &mut [u8] = unsafe { std::mem::transmute(buf) }; + let amt = buf.len().min(state.buffer.len()); + let amt = state.buffer.dequeue_slice(&mut buf[..amt]); + + if let Some(handler) = state.pull_handler.as_mut() { + handler.push_interest(InterestType::Writable); + } + state.wakers.drain(..).for_each(|w| w.wake()); + Ok(amt) + } + + pub fn set_max_size(&self, new_size: usize) { + let mut state = self.state.lock().unwrap(); + state.halt_immediate_poll_write = false; + + let mut existing: Vec = vec![0; state.buffer.len()]; + if !state.buffer.is_empty() { + let amt = state.buffer.dequeue_slice(&mut existing[..]); + existing.resize(amt, 0); + } + + state.buffer = RingBuffer::new(vec![0; new_size]); + if !existing.is_empty() { + let _ = state.buffer.enqueue_slice(&existing[..]); + } + } + + pub fn max_size(&self) -> usize { + let state = self.state.lock().unwrap(); + state.buffer.capacity() + } +} + +impl AsyncWrite for SocketBuffer { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + match self.try_send(buf, false, Some(cx.waker())) { + Ok(amt) => Poll::Ready(Ok(amt)), + Err(NetworkError::WouldBlock) => Poll::Pending, + Err(err) => Poll::Ready(Err(net_error_into_io_err(err))), + } + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + self.set_dead(); + Poll::Ready(Ok(())) + } +} + +impl AsyncRead for SocketBuffer { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut tokio::io::ReadBuf<'_>, + ) -> Poll> { + match self.try_read(unsafe { buf.unfilled_mut() }, Some(cx.waker())) { + Ok(amt) => { + unsafe { buf.assume_init(amt) }; + buf.advance(amt); + Poll::Ready(Ok(())) + } + Err(NetworkError::WouldBlock) => Poll::Pending, + Err(err) => Poll::Ready(Err(net_error_into_io_err(err))), + } + } +} + +#[derive(Debug)] +pub struct TcpSocketHalf { + addr_local: SocketAddr, + addr_peer: SocketAddr, + tx: SocketBuffer, + rx: SocketBuffer, + ttl: u32, +} + +impl TcpSocketHalf { + pub fn channel( + max_buffer_size: usize, + addr1: SocketAddr, + addr2: SocketAddr, + ) -> (TcpSocketHalf, TcpSocketHalf) { + let mut buffer1 = SocketBuffer::new(max_buffer_size); + buffer1.dead_on_drop = true; + + let mut buffer2 = SocketBuffer::new(max_buffer_size); + buffer2.dead_on_drop = true; + + let half1 = Self { + tx: buffer1.clone(), + rx: buffer2.clone(), + addr_local: addr1, + addr_peer: addr2, + ttl: 64, + }; + let half2 = Self { + tx: buffer2, + rx: buffer1, + addr_local: addr2, + addr_peer: addr1, + ttl: 64, + }; + (half1, half2) + } + + pub fn is_active(&self) -> bool { + self.tx.is_dead() == false + } + + pub fn close(&self) -> crate::Result<()> { + self.tx.set_dead(); + self.rx.set_dead(); + Ok(()) + } +} + +impl VirtualIoSource for TcpSocketHalf { + fn remove_handler(&mut self) { + self.tx.clear_pull_handler(); + self.rx.clear_push_handler(); + } + + fn poll_read_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.rx.poll_read_ready(cx) + } + + fn poll_write_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.tx.poll_write_ready(cx) + } +} + +impl VirtualSocket for TcpSocketHalf { + fn set_ttl(&mut self, ttl: u32) -> crate::Result<()> { + self.ttl = ttl; + Ok(()) + } + + fn ttl(&self) -> crate::Result { + Ok(self.ttl) + } + + fn addr_local(&self) -> crate::Result { + Ok(self.addr_local) + } + + fn status(&self) -> crate::Result { + Ok(if self.tx.is_dead() { + SocketStatus::Closed + } else { + SocketStatus::Opened + }) + } + + fn set_handler( + &mut self, + handler: Box, + ) -> crate::Result<()> { + let handler = ArcInterestHandler::new(handler); + self.tx.set_pull_handler(handler.clone()); + self.rx.set_push_handler(handler); + Ok(()) + } +} + +impl VirtualConnectedSocket for TcpSocketHalf { + fn set_linger(&mut self, _linger: Option) -> crate::Result<()> { + Ok(()) + } + + fn linger(&self) -> crate::Result> { + Ok(None) + } + + fn try_send(&mut self, data: &[u8]) -> crate::Result { + self.tx.try_send(data, false, None) + } + + fn try_flush(&mut self) -> crate::Result<()> { + Ok(()) + } + + fn close(&mut self) -> crate::Result<()> { + self.tx.set_dead(); + self.rx.set_dead(); + Ok(()) + } + + fn try_recv(&mut self, buf: &mut [std::mem::MaybeUninit]) -> crate::Result { + self.rx.try_read(buf, None) + } +} + +impl VirtualTcpSocket for TcpSocketHalf { + fn set_recv_buf_size(&mut self, size: usize) -> crate::Result<()> { + self.rx.set_max_size(size); + Ok(()) + } + + fn recv_buf_size(&self) -> crate::Result { + Ok(self.rx.max_size()) + } + + fn set_send_buf_size(&mut self, size: usize) -> crate::Result<()> { + self.tx.set_max_size(size); + Ok(()) + } + + fn send_buf_size(&self) -> crate::Result { + Ok(self.tx.max_size()) + } + + fn set_nodelay(&mut self, _reuse: bool) -> crate::Result<()> { + Ok(()) + } + + fn nodelay(&self) -> crate::Result { + Ok(true) + } + + fn set_keepalive(&mut self, _keepalive: bool) -> crate::Result<()> { + Ok(()) + } + + fn keepalive(&self) -> crate::Result { + Ok(false) + } + + fn set_dontroute(&mut self, _keepalive: bool) -> crate::Result<()> { + Ok(()) + } + + fn dontroute(&self) -> crate::Result { + Ok(false) + } + + fn addr_peer(&self) -> crate::Result { + Ok(self.addr_peer) + } + + fn shutdown(&mut self, how: std::net::Shutdown) -> crate::Result<()> { + match how { + std::net::Shutdown::Both => { + self.tx.set_dead(); + self.rx.set_dead(); + } + std::net::Shutdown::Read => { + self.rx.set_dead(); + } + std::net::Shutdown::Write => { + self.tx.set_dead(); + } + } + Ok(()) + } + + fn is_closed(&self) -> bool { + self.tx.is_dead() + } +} + +#[allow(unused)] +#[derive(Debug)] +pub struct TcpSocketHalfTx { + addr_local: SocketAddr, + addr_peer: SocketAddr, + tx: SocketBuffer, + ttl: u32, +} + +impl TcpSocketHalfTx { + pub fn poll_send(&self, cx: &mut Context<'_>, data: &[u8]) -> Poll> { + match self.tx.try_send(data, false, Some(cx.waker())) { + Ok(amt) => Poll::Ready(Ok(amt)), + Err(NetworkError::WouldBlock) => Poll::Pending, + Err(err) => Poll::Ready(Err(net_error_into_io_err(err))), + } + } + + pub fn try_send(&self, data: &[u8]) -> io::Result { + self.tx + .try_send(data, false, None) + .map_err(net_error_into_io_err) + } + + pub async fn send_ext(&self, data: Bytes, non_blocking: bool) -> io::Result<()> { + if non_blocking { + self.tx + .try_send(&data, true, None) + .map_err(net_error_into_io_err) + .map(|_| ()) + } else { + self.tx.send(data).await.map_err(net_error_into_io_err) + } + } + + pub async fn send(&self, data: Bytes) -> io::Result<()> { + self.send_ext(data, false).await + } + + pub fn close(&self) -> crate::Result<()> { + self.tx.set_dead(); + Ok(()) + } +} + +impl AsyncWrite for TcpSocketHalfTx { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut self.tx).poll_write(cx, buf) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.tx).poll_flush(cx) + } + + fn poll_shutdown( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + Pin::new(&mut self.tx).poll_shutdown(cx) + } +} + +#[allow(unused)] +#[derive(Debug)] +pub struct TcpSocketHalfRx { + addr_local: SocketAddr, + addr_peer: SocketAddr, + rx: BufReader, + ttl: u32, +} + +impl TcpSocketHalfRx { + pub fn buffer(&self) -> &[u8] { + self.rx.buffer() + } + + pub fn close(&mut self) -> crate::Result<()> { + self.rx.get_mut().set_dead(); + Ok(()) + } + + #[allow(dead_code)] + pub(crate) fn inner(&mut self) -> &BufReader { + &self.rx + } + + #[allow(dead_code)] + pub(crate) fn inner_mut(&mut self) -> &mut BufReader { + &mut self.rx + } +} + +impl AsyncRead for TcpSocketHalfRx { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut tokio::io::ReadBuf<'_>, + ) -> Poll> { + Pin::new(&mut self.rx).poll_read(cx, buf) + } +} + +impl TcpSocketHalfRx { + pub fn poll_fill_buf(&mut self, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.rx).poll_fill_buf(cx) + } + + pub fn consume(&mut self, amt: usize) { + Pin::new(&mut self.rx).consume(amt) + } +} + +impl TcpSocketHalf { + pub fn split(self) -> (TcpSocketHalfTx, TcpSocketHalfRx) { + let tx = TcpSocketHalfTx { + tx: self.tx, + addr_local: self.addr_local, + addr_peer: self.addr_peer, + ttl: self.ttl, + }; + let rx = TcpSocketHalfRx { + rx: BufReader::new(self.rx), + addr_local: self.addr_local, + addr_peer: self.addr_peer, + ttl: self.ttl, + }; + (tx, rx) + } + + pub fn combine(tx: TcpSocketHalfTx, rx: TcpSocketHalfRx) -> Self { + Self { + tx: tx.tx, + rx: rx.rx.into_inner(), + addr_local: tx.addr_local, + addr_peer: tx.addr_peer, + ttl: tx.ttl, + } + } +} diff --git a/lib/wasix/Cargo.toml b/lib/wasix/Cargo.toml index e574df659e2..388a338c234 100644 --- a/lib/wasix/Cargo.toml +++ b/lib/wasix/Cargo.toml @@ -141,6 +141,7 @@ time = ["tokio/time"] webc_runner_rt_wcgi = ["hyper", "wcgi", "wcgi-host", "tower", "tower-http"] webc_runner_rt_dcgi = ["webc_runner_rt_wcgi", "journal"] +webc_runner_rt_dproxy = ["hyper", "tower", "tower-http", "journal"] webc_runner_rt_emscripten = ["wasmer-emscripten"] sys = ["webc/mmap", "time", "virtual-mio/sys"] @@ -193,6 +194,7 @@ features = [ "wasmer/sys", "webc_runner_rt_wcgi", "webc_runner_rt_dcgi", + "webc_runner_rt_dproxy", "webc_runner_rt_emscripten", "sys-default", ] diff --git a/lib/wasix/src/journal/concrete/archived.rs b/lib/wasix/src/journal/concrete/archived.rs index b5eb9852d5e..00f67bbdc3f 100644 --- a/lib/wasix/src/journal/concrete/archived.rs +++ b/lib/wasix/src/journal/concrete/archived.rs @@ -3,8 +3,9 @@ use num_enum::{IntoPrimitive, TryFromPrimitive}; use rkyv::ser::{ScratchSpace, Serializer}; use rkyv::{Archive, CheckBytes, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; use std::borrow::Cow; -use std::time::SystemTime; -use virtual_net::{Duration, IpAddr, IpCidr, Ipv4Addr, Ipv6Addr, SocketAddr, StreamSecurity}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use std::time::{Duration, SystemTime}; +use virtual_net::{IpCidr, StreamSecurity}; use wasmer_wasix_types::wasi::{self, EpollEventCtl, EpollType, Fdflags, Rights, Sockoption}; use super::*; diff --git a/lib/wasix/src/journal/effector/syscalls/port_addr_remove.rs b/lib/wasix/src/journal/effector/syscalls/port_addr_remove.rs index 1463d2595cb..b2d46f926db 100644 --- a/lib/wasix/src/journal/effector/syscalls/port_addr_remove.rs +++ b/lib/wasix/src/journal/effector/syscalls/port_addr_remove.rs @@ -1,4 +1,4 @@ -use virtual_net::IpAddr; +use std::net::IpAddr; use super::*; diff --git a/lib/wasix/src/journal/effector/syscalls/port_gateway_set.rs b/lib/wasix/src/journal/effector/syscalls/port_gateway_set.rs index 230eba8d467..a0251a02e88 100644 --- a/lib/wasix/src/journal/effector/syscalls/port_gateway_set.rs +++ b/lib/wasix/src/journal/effector/syscalls/port_gateway_set.rs @@ -1,4 +1,4 @@ -use virtual_net::IpAddr; +use std::net::IpAddr; use super::*; diff --git a/lib/wasix/src/journal/effector/syscalls/port_route_add.rs b/lib/wasix/src/journal/effector/syscalls/port_route_add.rs index 0565e8b8f09..d70a3f6871e 100644 --- a/lib/wasix/src/journal/effector/syscalls/port_route_add.rs +++ b/lib/wasix/src/journal/effector/syscalls/port_route_add.rs @@ -1,4 +1,6 @@ -use virtual_net::{Duration, IpAddr, IpCidr}; +use std::{net::IpAddr, time::Duration}; + +use virtual_net::IpCidr; use super::*; diff --git a/lib/wasix/src/journal/effector/syscalls/port_route_remove.rs b/lib/wasix/src/journal/effector/syscalls/port_route_remove.rs index 61d371b8da1..3a980bfff65 100644 --- a/lib/wasix/src/journal/effector/syscalls/port_route_remove.rs +++ b/lib/wasix/src/journal/effector/syscalls/port_route_remove.rs @@ -1,4 +1,4 @@ -use virtual_net::IpAddr; +use std::net::IpAddr; use super::*; diff --git a/lib/wasix/src/journal/effector/syscalls/sock_accept.rs b/lib/wasix/src/journal/effector/syscalls/sock_accept.rs index 2bbbf3b382d..3482074dcfe 100644 --- a/lib/wasix/src/journal/effector/syscalls/sock_accept.rs +++ b/lib/wasix/src/journal/effector/syscalls/sock_accept.rs @@ -1,4 +1,4 @@ -use virtual_net::SocketAddr; +use std::net::SocketAddr; use crate::{ fs::Kind, diff --git a/lib/wasix/src/journal/effector/syscalls/sock_bind.rs b/lib/wasix/src/journal/effector/syscalls/sock_bind.rs index fc0727e788b..73c3e3a7b81 100644 --- a/lib/wasix/src/journal/effector/syscalls/sock_bind.rs +++ b/lib/wasix/src/journal/effector/syscalls/sock_bind.rs @@ -1,4 +1,4 @@ -use virtual_net::SocketAddr; +use std::net::SocketAddr; use super::*; diff --git a/lib/wasix/src/journal/effector/syscalls/sock_connect.rs b/lib/wasix/src/journal/effector/syscalls/sock_connect.rs index 5e4b2f89b6b..3c7af982efd 100644 --- a/lib/wasix/src/journal/effector/syscalls/sock_connect.rs +++ b/lib/wasix/src/journal/effector/syscalls/sock_connect.rs @@ -1,4 +1,4 @@ -use virtual_net::SocketAddr; +use std::net::SocketAddr; use crate::{ fs::Kind, diff --git a/lib/wasix/src/journal/effector/syscalls/sock_join_ipv4_multicast.rs b/lib/wasix/src/journal/effector/syscalls/sock_join_ipv4_multicast.rs index a1c4d40005d..589bb08cafd 100644 --- a/lib/wasix/src/journal/effector/syscalls/sock_join_ipv4_multicast.rs +++ b/lib/wasix/src/journal/effector/syscalls/sock_join_ipv4_multicast.rs @@ -1,4 +1,4 @@ -use virtual_net::Ipv4Addr; +use std::net::Ipv4Addr; use super::*; diff --git a/lib/wasix/src/journal/effector/syscalls/sock_join_ipv6_multicast.rs b/lib/wasix/src/journal/effector/syscalls/sock_join_ipv6_multicast.rs index d130e0391f1..17e7d875799 100644 --- a/lib/wasix/src/journal/effector/syscalls/sock_join_ipv6_multicast.rs +++ b/lib/wasix/src/journal/effector/syscalls/sock_join_ipv6_multicast.rs @@ -1,4 +1,4 @@ -use virtual_net::Ipv6Addr; +use std::net::Ipv6Addr; use super::*; diff --git a/lib/wasix/src/journal/effector/syscalls/sock_leave_ipv4_multicast.rs b/lib/wasix/src/journal/effector/syscalls/sock_leave_ipv4_multicast.rs index 50abd7d6255..9fd3b78eba5 100644 --- a/lib/wasix/src/journal/effector/syscalls/sock_leave_ipv4_multicast.rs +++ b/lib/wasix/src/journal/effector/syscalls/sock_leave_ipv4_multicast.rs @@ -1,4 +1,4 @@ -use virtual_net::Ipv4Addr; +use std::net::Ipv4Addr; use super::*; diff --git a/lib/wasix/src/journal/effector/syscalls/sock_leave_ipv6_multicast.rs b/lib/wasix/src/journal/effector/syscalls/sock_leave_ipv6_multicast.rs index 6d7fad5fea1..b91f71093bc 100644 --- a/lib/wasix/src/journal/effector/syscalls/sock_leave_ipv6_multicast.rs +++ b/lib/wasix/src/journal/effector/syscalls/sock_leave_ipv6_multicast.rs @@ -1,4 +1,4 @@ -use virtual_net::Ipv6Addr; +use std::net::Ipv6Addr; use super::*; diff --git a/lib/wasix/src/journal/effector/syscalls/sock_send_to.rs b/lib/wasix/src/journal/effector/syscalls/sock_send_to.rs index 7df19d13eb0..f1e7608e5d6 100644 --- a/lib/wasix/src/journal/effector/syscalls/sock_send_to.rs +++ b/lib/wasix/src/journal/effector/syscalls/sock_send_to.rs @@ -1,4 +1,4 @@ -use virtual_net::SocketAddr; +use std::net::SocketAddr; use wasmer_wasix_types::wasi::SiFlags; use crate::syscalls::sock_send_to_internal; diff --git a/lib/wasix/src/journal/effector/syscalls/sock_set_opt_time.rs b/lib/wasix/src/journal/effector/syscalls/sock_set_opt_time.rs index 75574fb5cde..0dbdf7ab339 100644 --- a/lib/wasix/src/journal/effector/syscalls/sock_set_opt_time.rs +++ b/lib/wasix/src/journal/effector/syscalls/sock_set_opt_time.rs @@ -1,4 +1,4 @@ -use virtual_net::Duration; +use std::time::Duration; use crate::net::socket::TimeType; diff --git a/lib/wasix/src/journal/entry.rs b/lib/wasix/src/journal/entry.rs index 37bb494163f..ed3913c7c48 100644 --- a/lib/wasix/src/journal/entry.rs +++ b/lib/wasix/src/journal/entry.rs @@ -1,10 +1,11 @@ use super::base64; use derivative::Derivative; use serde::{Deserialize, Serialize}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::net::{Shutdown, SocketAddr}; -use std::time::SystemTime; +use std::time::{Duration, SystemTime}; use std::{borrow::Cow, ops::Range}; -use virtual_net::{Duration, IpAddr, IpCidr, Ipv4Addr, Ipv6Addr, StreamSecurity}; +use virtual_net::{IpCidr, StreamSecurity}; use wasmer_wasix_types::wasi::{ Addressfamily, Advice, EpollCtl, EpollEventCtl, EventFdFlags, ExitCode, Fdflags, FileDelta, Filesize, Fstflags, LookupFlags, Oflags, Rights, SiFlags, Snapshot0Clockid, SockProto, diff --git a/lib/wasix/src/runners/dproxy/factory.rs b/lib/wasix/src/runners/dproxy/factory.rs new file mode 100644 index 00000000000..0d4d936c10d --- /dev/null +++ b/lib/wasix/src/runners/dproxy/factory.rs @@ -0,0 +1,102 @@ +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, + time::Instant, +}; + +use derivative::Derivative; + +use crate::{ + runners::Runner, + runtime::{DynRuntime, OverriddenRuntime}, +}; + +use super::{ + handler::Handler, hyper_proxy::HyperProxyConnectorBuilder, instance::DProxyInstance, + networking::LocalWithLoopbackNetworking, shard::Shard, socket_manager::SocketManager, +}; + +#[derive(Debug, Default)] +struct State { + instance: HashMap, +} + +/// This factory will store and reuse instances between invocations thus +/// allowing for the instances to be stateful. +#[derive(Derivative, Clone, Default)] +#[derivative(Debug)] +pub struct DProxyInstanceFactory { + state: Arc>, +} + +impl DProxyInstanceFactory { + pub fn new() -> Self { + Default::default() + } + + pub async fn acquire(&self, handler: &Handler, shard: Shard) -> anyhow::Result { + loop { + { + let state = self.state.lock().unwrap(); + if let Some(instance) = state.instance.get(&shard).cloned() { + return Ok(instance); + } + } + + let instance = self.spin_up(handler, shard.clone()).await?; + + let mut state = self.state.lock().unwrap(); + state.instance.insert(shard.clone(), instance); + } + } + + pub async fn spin_up( + &self, + handler: &Handler, + _shard: Shard, + ) -> anyhow::Result { + // Get the runtime with its already wired local networking + let runtime = handler.runtime.clone(); + + // DProxy is able to resume execution of the stateful workload using memory + // snapshots hence the journals it stores are complete journals + let journals = runtime.journals().clone().into_iter().collect::>(); + let mut runtime = OverriddenRuntime::new(runtime).with_journals(journals); + + // We attach a composite networking to the runtime which includes a loopback + // networking implementation connected to a socket manager + let composite_networking = LocalWithLoopbackNetworking::new(); + let socket_manager = Arc::new(SocketManager::new( + composite_networking.loopback_networking(), + handler.config.proxy_connect_init_timeout, + handler.config.proxy_connect_nominal_timeout, + handler.config.max_socket_pool_size, + )); + runtime = runtime.with_networking(Arc::new(composite_networking)); + + // The connector uses the socket manager to open sockets to the instance + let connector = HyperProxyConnectorBuilder::new(socket_manager.clone()) + .with_reuse(true) + .build() + .await; + + // Now we run the actual instance under a WasiRunner + let pkg = handler.config.pkg.clone(); + let command_name = handler.command_name.clone(); + let runtime = Arc::new(runtime) as Arc; + let mut runner = handler.config.inner.clone(); + runtime + .task_manager() + .clone() + .task_dedicated(Box::new(move || { + runner.run_command(&command_name, &pkg, runtime); + })); + + // Return an instance + Ok(DProxyInstance { + last_used: Arc::new(Mutex::new(Instant::now())), + socket_manager, + client: hyper::Client::builder().build(connector), + }) + } +} diff --git a/lib/wasix/src/runners/dproxy/handler.rs b/lib/wasix/src/runners/dproxy/handler.rs new file mode 100644 index 00000000000..beff4d554a9 --- /dev/null +++ b/lib/wasix/src/runners/dproxy/handler.rs @@ -0,0 +1,121 @@ +use std::pin::Pin; +use std::sync::Arc; +use std::task::Poll; + +use futures::{Future, FutureExt}; +use http::{Request, Response, Uri}; +use hyper::Body; +use tower::Service; + +use crate::runners::dproxy::shard::Shard; +use crate::Runtime; + +use super::factory::DProxyInstanceFactory; +use super::Config; + +#[derive(derivative::Derivative)] +#[derivative(Debug)] +pub struct SharedState { + pub(crate) config: Config, + pub(crate) command_name: String, + #[derivative(Debug = "ignore")] + pub(crate) runtime: Arc, + pub(crate) factory: DProxyInstanceFactory, +} + +/// Handler which will process DProxy requests +#[derive(Clone, Debug)] +pub(crate) struct Handler(Arc); + +impl Handler { + pub(crate) fn new( + config: Config, + command_name: String, + factory: DProxyInstanceFactory, + runtime: Arc, + ) -> Self { + Handler(Arc::new(SharedState { + config, + command_name, + runtime, + factory, + })) + } + + #[tracing::instrument(level = "debug", skip_all, err)] + pub(crate) async fn handle( + &self, + mut req: Request, + _token: T, + ) -> anyhow::Result> + where + T: Send + 'static, + { + tracing::debug!(headers=?req.headers()); + + // Determine the shard we are using + let shard = req + .headers() + .get("X-Shard") + .map(|v| String::from_utf8_lossy(v.as_bytes())) + .map(|s| match u64::from_str_radix(&s, 10) { + Ok(id) => Ok(Shard::ById(id)), + Err(err) => Err(err), + }) + .unwrap_or(Ok(Shard::Singleton))?; + + // Modify the request URI so that it will work with the hyper proxy + let mut new_uri = Uri::builder() + .scheme("http") + .authority( + req.uri() + .authority() + .cloned() + .unwrap_or_else(|| "localhost".parse().unwrap()), + ) + .path_and_query( + req.uri() + .path_and_query() + .cloned() + .unwrap_or_else(|| "/".parse().unwrap()), + ) + .build() + .unwrap(); + std::mem::swap(req.uri_mut(), &mut new_uri); + + // Acquire a DProxy instance + tracing::debug!("Acquiring DProxy instance instance"); + let instance = self.factory.acquire(self, shard).await?; + + tracing::debug!("Calling into the DProxy instance"); + let client = instance.client.clone(); + + // Perform the request + Ok(client.request(req).await?) + } +} + +impl std::ops::Deref for Handler { + type Target = Arc; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Service> for Handler { + type Response = Response; + type Error = anyhow::Error; + type Future = Pin>> + Send>>; + + fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, request: Request) -> Self::Future { + // Note: all fields are reference-counted so cloning is pretty cheap + let handler = self.clone(); + let fut = async move { handler.handle(request, ()).await }; + fut.boxed() + } +} diff --git a/lib/wasix/src/runners/dproxy/hyper_proxy/builder.rs b/lib/wasix/src/runners/dproxy/hyper_proxy/builder.rs new file mode 100644 index 00000000000..d8f914517f8 --- /dev/null +++ b/lib/wasix/src/runners/dproxy/hyper_proxy/builder.rs @@ -0,0 +1,32 @@ +use std::sync::Arc; + +use crate::runners::dproxy::socket_manager::SocketManager; + +use super::*; + +#[derive(Debug)] +pub struct HyperProxyConnectorBuilder { + socket_manager: Arc, + reuse: bool, +} + +impl HyperProxyConnectorBuilder { + pub fn new(socket_manager: Arc) -> Self { + Self { + socket_manager, + reuse: true, + } + } + + pub fn with_reuse(mut self, reuse: bool) -> Self { + self.reuse = reuse; + self + } + + pub async fn build(self) -> HyperProxyConnector { + HyperProxyConnector { + socket_manager: self.socket_manager, + reuse: self.reuse, + } + } +} diff --git a/lib/wasix/src/runners/dproxy/hyper_proxy/connector.rs b/lib/wasix/src/runners/dproxy/hyper_proxy/connector.rs new file mode 100644 index 00000000000..473dbab1c80 --- /dev/null +++ b/lib/wasix/src/runners/dproxy/hyper_proxy/connector.rs @@ -0,0 +1,39 @@ +use std::sync::Arc; + +use super::socket_manager::SocketManager; + +use super::*; + +/// A Connector for the WASM processes behind a socket. +#[derive(Debug, Clone)] +pub struct HyperProxyConnector { + pub(super) socket_manager: Arc, + pub(super) reuse: bool, +} + +impl HyperProxyConnector { + pub fn socket_manager(&self) -> &Arc { + &self.socket_manager + } +} + +impl Service for HyperProxyConnector { + type Response = HyperProxyStream; + type Error = BoxError; + + #[allow(clippy::type_complexity)] + type Future = Pin> + Send>>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, _dst: Uri) -> Self::Future { + let this = self.clone(); + Box::pin(async move { + let socket = this.socket_manager.acquire_http_socket(this.reuse).await?; + let (tx, rx) = socket.split(); + Ok(HyperProxyStream { tx, rx }) + }) + } +} diff --git a/lib/wasix/src/runners/dproxy/hyper_proxy/mod.rs b/lib/wasix/src/runners/dproxy/hyper_proxy/mod.rs new file mode 100644 index 00000000000..5dcc7c785b4 --- /dev/null +++ b/lib/wasix/src/runners/dproxy/hyper_proxy/mod.rs @@ -0,0 +1,18 @@ +mod builder; +mod connector; +mod stream; + +pub use builder::*; +pub use connector::*; +pub use stream::*; + +use super::*; + +pub(super) use hyper::{service::Service, Uri}; +pub(super) use std::pin::Pin; +pub(super) type BoxError = Box; +pub(super) use std::{ + future::Future, + task::{Context, Poll}, +}; +pub(super) use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; diff --git a/lib/wasix/src/runners/dproxy/hyper_proxy/stream.rs b/lib/wasix/src/runners/dproxy/hyper_proxy/stream.rs new file mode 100644 index 00000000000..65136e78937 --- /dev/null +++ b/lib/wasix/src/runners/dproxy/hyper_proxy/stream.rs @@ -0,0 +1,53 @@ +use std::io; + +use hyper::client::connect::Connected; +use virtual_net::{TcpSocketHalfRx, TcpSocketHalfTx}; + +use super::*; + +#[derive(Debug)] +pub struct HyperProxyStream { + pub(super) tx: TcpSocketHalfTx, + pub(super) rx: TcpSocketHalfRx, +} + +impl AsyncRead for HyperProxyStream { + #[inline] + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + Pin::new(&mut self.rx).poll_read(cx, buf) + } +} + +impl AsyncWrite for HyperProxyStream { + #[inline] + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut self.tx).poll_write(cx, buf) + } + + #[inline] + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.tx).poll_flush(cx) + } + + #[inline] + fn poll_shutdown( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + Pin::new(&mut self.tx).poll_shutdown(cx) + } +} + +impl hyper::client::connect::Connection for HyperProxyStream { + fn connected(&self) -> Connected { + Connected::new().proxy(true) + } +} diff --git a/lib/wasix/src/runners/dproxy/instance.rs b/lib/wasix/src/runners/dproxy/instance.rs new file mode 100644 index 00000000000..6e856e5cf0b --- /dev/null +++ b/lib/wasix/src/runners/dproxy/instance.rs @@ -0,0 +1,13 @@ +use std::{ + sync::{Arc, Mutex}, + time::Instant, +}; + +use super::{hyper_proxy::HyperProxyConnector, socket_manager::SocketManager}; + +#[derive(Debug, Clone)] +pub(crate) struct DProxyInstance { + pub(super) last_used: Arc>, + pub(super) socket_manager: Arc, + pub(super) client: hyper::Client, +} diff --git a/lib/wasix/src/runners/dproxy/mod.rs b/lib/wasix/src/runners/dproxy/mod.rs new file mode 100644 index 00000000000..b4408004f7d --- /dev/null +++ b/lib/wasix/src/runners/dproxy/mod.rs @@ -0,0 +1,10 @@ +mod factory; +pub(super) mod handler; +mod hyper_proxy; +mod instance; +mod networking; +mod runner; +mod shard; +mod socket_manager; + +pub use self::runner::{Config, DProxyRunner}; diff --git a/lib/wasix/src/runners/dproxy/networking.rs b/lib/wasix/src/runners/dproxy/networking.rs new file mode 100644 index 00000000000..fd78f6eba9c --- /dev/null +++ b/lib/wasix/src/runners/dproxy/networking.rs @@ -0,0 +1,258 @@ +use std::{ + net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, + sync::{Arc, Mutex}, + task::Waker, + time::Duration, +}; + +use virtual_net::{ + host::LocalNetworking, loopback::LoopbackNetworking, CompositeTcpListener, IpCidr, IpRoute, + NetworkError, StreamSecurity, VirtualIcmpSocket, VirtualNetworking, VirtualRawSocket, + VirtualTcpListener, VirtualTcpSocket, VirtualUdpSocket, +}; + +#[derive(Debug, Default)] +struct LocalWithLoopbackNetworkingListening { + addresses: Vec, + wakers: Vec, +} + +#[derive(Debug)] +pub struct LocalWithLoopbackNetworking { + inner_networking: Arc, + listen_port: Option, + local_listening: Arc>, + loopback_networking: LoopbackNetworking, +} + +impl LocalWithLoopbackNetworking { + pub fn new() -> Self { + lazy_static::lazy_static! { + static ref LOCAL_NETWORKING: Arc = Arc::new(LocalNetworking::default()); + } + Self { + local_listening: Default::default(), + listen_port: None, + inner_networking: LOCAL_NETWORKING.clone(), + loopback_networking: LoopbackNetworking::new(), + } + } + + pub fn register_listener(&self, addr: SocketAddr) { + let mut listening = self.local_listening.lock().unwrap(); + listening.addresses.push(addr); + listening.addresses.sort_by_key(|a| a.port()); + listening.wakers.drain(..).for_each(|w| w.wake()); + } + + pub fn loopback_networking(&self) -> LoopbackNetworking { + self.loopback_networking.clone() + } +} + +#[allow(unused_variables)] +#[async_trait::async_trait] +impl VirtualNetworking for LocalWithLoopbackNetworking { + /// Bridges this local network with a remote network, which is required in + /// order to make lower level networking calls (such as UDP/TCP) + async fn bridge( + &self, + network: &str, + access_token: &str, + security: StreamSecurity, + ) -> Result<(), NetworkError> { + self.inner_networking + .bridge(network, access_token, security) + .await + } + + /// Disconnects from the remote network essentially unbridging it + async fn unbridge(&self) -> Result<(), NetworkError> { + self.inner_networking.unbridge().await + } + + /// Acquires an IP address on the network and configures the routing tables + async fn dhcp_acquire(&self) -> Result, NetworkError> { + self.inner_networking.dhcp_acquire().await + } + + /// Adds a static IP address to the interface with a netmask prefix + async fn ip_add(&self, ip: IpAddr, prefix: u8) -> Result<(), NetworkError> { + self.inner_networking.ip_add(ip, prefix).await + } + + /// Removes a static (or dynamic) IP address from the interface + async fn ip_remove(&self, ip: IpAddr) -> Result<(), NetworkError> { + self.inner_networking.ip_remove(ip).await + } + + /// Clears all the assigned IP addresses for this interface + async fn ip_clear(&self) -> Result<(), NetworkError> { + self.inner_networking.ip_clear().await + } + + /// Lists all the IP addresses currently assigned to this interface + async fn ip_list(&self) -> Result, NetworkError> { + self.inner_networking.ip_list().await + } + + /// Returns the hardware MAC address for this interface + async fn mac(&self) -> Result<[u8; 6], NetworkError> { + self.inner_networking.mac().await + } + + /// Adds a default gateway to the routing table + async fn gateway_set(&self, ip: IpAddr) -> Result<(), NetworkError> { + self.inner_networking.gateway_set(ip).await + } + + /// Adds a specific route to the routing table + async fn route_add( + &self, + cidr: IpCidr, + via_router: IpAddr, + preferred_until: Option, + expires_at: Option, + ) -> Result<(), NetworkError> { + self.inner_networking + .route_add(cidr, via_router, preferred_until, expires_at) + .await + } + + /// Removes a routing rule from the routing table + async fn route_remove(&self, cidr: IpAddr) -> Result<(), NetworkError> { + self.inner_networking.route_remove(cidr).await + } + + /// Clears the routing table for this interface + async fn route_clear(&self) -> Result<(), NetworkError> { + self.inner_networking.route_clear().await + } + + /// Lists all the routes defined in the routing table for this interface + async fn route_list(&self) -> Result, NetworkError> { + self.inner_networking.route_list().await + } + + /// Creates a low level socket that can read and write Ethernet packets + /// directly to the interface + async fn bind_raw(&self) -> Result, NetworkError> { + self.inner_networking.bind_raw().await + } + + /// Listens for TCP connections on a specific IP and Port combination + /// Multiple servers (processes or threads) can bind to the same port if they each set + /// the reuse-port and-or reuse-addr flags + async fn listen_tcp( + &self, + addr: SocketAddr, + only_v6: bool, + reuse_port: bool, + reuse_addr: bool, + ) -> Result, NetworkError> { + // We determine if the listener should be registered so that + // anyone waiting to start a connection attempt from the proxy + // can do so + let add_listener = if let Some(special_port) = self.listen_port { + addr.port() == special_port + } else { + true + }; + + let backlog = 1024; + + let ret: Result, NetworkError> = + if is_ip_unspecified(&addr.ip()) { + let mut socket = CompositeTcpListener::new(); + socket.add_port( + self.loopback_networking + .listen_tcp(addr, only_v6, reuse_port, reuse_addr) + .await?, + ); + socket.add_port( + self.inner_networking + .listen_tcp(addr, only_v6, reuse_port, reuse_addr) + .await?, + ); + Ok(Box::new(socket)) + } else if is_ip_loopback(&addr.ip()) { + self.loopback_networking + .listen_tcp(addr, only_v6, reuse_port, reuse_addr) + .await + } else { + self.inner_networking + .listen_tcp(addr, only_v6, reuse_port, reuse_addr) + .await + }; + + if add_listener { + self.register_listener(addr); + } + + ret + } + + /// Opens a UDP socket that listens on a specific IP and Port combination + /// Multiple servers (processes or threads) can bind to the same port if they each set + /// the reuse-port and-or reuse-addr flags + async fn bind_udp( + &self, + addr: SocketAddr, + reuse_port: bool, + reuse_addr: bool, + ) -> Result, NetworkError> { + self.inner_networking + .bind_udp(addr, reuse_port, reuse_addr) + .await + } + + /// Creates a socket that can be used to send and receive ICMP packets + /// from a paritcular IP address + async fn bind_icmp( + &self, + addr: IpAddr, + ) -> Result, NetworkError> { + self.inner_networking.bind_icmp(addr).await + } + + /// Opens a TCP connection to a particular destination IP address and port + async fn connect_tcp( + &self, + addr: SocketAddr, + peer: SocketAddr, + ) -> Result, NetworkError> { + self.inner_networking.connect_tcp(addr, peer).await + } + + /// Performs DNS resolution for a specific hostname + async fn resolve( + &self, + host: &str, + port: Option, + dns_server: Option, + ) -> Result, NetworkError> { + self.inner_networking.resolve(host, port, dns_server).await + } +} + +pub const fn is_ip_unspecified(ip: &IpAddr) -> bool { + match ip { + IpAddr::V4(ip) => ip.is_unspecified(), + IpAddr::V6(ip) => ip.is_unspecified(), + } +} + +pub const fn is_ip_loopback(ip: &IpAddr) -> bool { + match ip { + IpAddr::V4(ip) => is_ip4_loopback(ip), + IpAddr::V6(ip) => is_ip6_loopback(ip), + } +} + +pub const fn is_ip4_loopback(ip: &Ipv4Addr) -> bool { + ip.is_loopback() +} + +pub const fn is_ip6_loopback(ip: &Ipv6Addr) -> bool { + ip.is_loopback() +} diff --git a/lib/wasix/src/runners/dproxy/runner.rs b/lib/wasix/src/runners/dproxy/runner.rs new file mode 100644 index 00000000000..a0318c5d2b1 --- /dev/null +++ b/lib/wasix/src/runners/dproxy/runner.rs @@ -0,0 +1,147 @@ +use std::{net::SocketAddr, sync::Arc, time::Duration}; + +use anyhow::{Context, Error}; +use http::{Request, Response}; +use hyper::Body; +use tower::{make::Shared, ServiceBuilder}; +use tower_http::{catch_panic::CatchPanicLayer, cors::CorsLayer, trace::TraceLayer}; +use tracing::Span; +use webc::metadata::Command; + +use crate::{ + bin_factory::BinaryPackage, + runners::wasi::WasiRunner, + runtime::{task_manager::VirtualTaskManagerExt, DynRuntime}, +}; + +use super::factory::DProxyInstanceFactory; + +#[derive(Debug)] +pub struct DProxyRunner { + config: Config, + factory: DProxyInstanceFactory, +} + +impl DProxyRunner { + pub fn new(inner: WasiRunner, pkg: &BinaryPackage, uses: Vec) -> Self { + Self { + config: Config::new(inner, pkg, uses), + factory: DProxyInstanceFactory::new(), + } + } + + pub fn config(&mut self) -> &mut Config { + &mut self.config + } +} + +/// The base URI used by a [`DProxy`] runner. +pub const DPROXY_RUNNER_URI: &str = "https://webc.org/runner/dproxy"; + +impl crate::runners::Runner for DProxyRunner { + fn can_run_command(command: &Command) -> Result { + Ok(command.runner.starts_with(DPROXY_RUNNER_URI)) + } + + fn run_command( + &mut self, + command_name: &str, + _pkg: &BinaryPackage, + runtime: Arc, + ) -> Result<(), Error> { + // Create the handler that will process the HTTP requests + let handler = super::handler::Handler::new( + self.config.clone(), + command_name.to_string(), + self.factory.clone(), + runtime.clone(), + ); + + // We create a HTTP server which will reverse proxy all the requests + // to the proxy workload + let service = ServiceBuilder::new() + .layer( + TraceLayer::new_for_http() + .make_span_with(|request: &Request| { + tracing::info_span!( + "request", + method = %request.method(), + uri = %request.uri(), + status_code = tracing::field::Empty, + ) + }) + .on_response(|response: &Response<_>, _latency: Duration, span: &Span| { + span.record("status_code", &tracing::field::display(response.status())); + tracing::info!("response generated") + }), + ) + .layer(CatchPanicLayer::new()) + .layer(CorsLayer::permissive()) + .service(handler); + + let address = self.config.addr; + tracing::info!(%address, "Starting the DProxy server"); + + runtime + .task_manager() + .spawn_and_block_on(async move { + let (shutdown, _abort_handle) = + futures::future::abortable(futures::future::pending::<()>()); + + hyper::Server::bind(&address) + .serve(Shared::new(service)) + .with_graceful_shutdown(async { + let _ = shutdown.await; + tracing::info!("Shutting down gracefully"); + }) + .await + }) + .context("Unable to start the server")?; + + Ok(()) + } +} + +#[derive(Debug, Clone)] +pub struct Config { + pub(crate) inner: WasiRunner, + pub(crate) addr: SocketAddr, + pub(crate) pkg: BinaryPackage, + pub(crate) uses: Vec, + pub(crate) proxy_connect_init_timeout: Duration, + pub(crate) proxy_connect_nominal_timeout: Duration, + pub(crate) max_socket_pool_size: usize, +} + +impl Config { + pub fn new(inner: WasiRunner, pkg: &BinaryPackage, uses: Vec) -> Self { + Self { + inner, + pkg: pkg.clone(), + uses, + addr: ([127, 0, 0, 1], 8000).into(), + proxy_connect_init_timeout: Duration::from_secs(30), + proxy_connect_nominal_timeout: Duration::from_secs(30), + max_socket_pool_size: 50, + } + } + + pub fn addr(&mut self, addr: SocketAddr) -> &mut Self { + self.addr = addr; + self + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn send_and_sync() { + fn assert_send() {} + fn assert_sync() {} + + assert_send::(); + assert_sync::(); + } +} diff --git a/lib/wasix/src/runners/dproxy/shard.rs b/lib/wasix/src/runners/dproxy/shard.rs new file mode 100644 index 00000000000..bcea1333ae3 --- /dev/null +++ b/lib/wasix/src/runners/dproxy/shard.rs @@ -0,0 +1,6 @@ +#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Shard { + #[default] + Singleton, + ById(u64), +} diff --git a/lib/wasix/src/runners/dproxy/socket_manager.rs b/lib/wasix/src/runners/dproxy/socket_manager.rs new file mode 100644 index 00000000000..d855d458138 --- /dev/null +++ b/lib/wasix/src/runners/dproxy/socket_manager.rs @@ -0,0 +1,164 @@ +use std::{ + net::{IpAddr, Ipv4Addr, SocketAddr}, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + time::Duration, +}; + +use tokio::sync::mpsc::{ + self, + error::{TryRecvError, TrySendError}, +}; +use virtual_net::{LoopbackNetworking, TcpSocketHalf}; + +#[derive(Debug)] +pub struct SocketManager { + loopback_networking: LoopbackNetworking, + idle_http_sockets: tokio::sync::Mutex>, + send_socket_idle: Arc>, + proxy_connect_init_timeout: Duration, + proxy_connect_nominal_timeout: Duration, + maximum_idle_size: usize, + is_running: AtomicBool, +} + +impl SocketManager { + pub fn new( + loopback_networking: LoopbackNetworking, + proxy_connect_init_timeout: Duration, + proxy_connect_nominal_timeout: Duration, + maximum_idle_size: usize, + ) -> Self { + let maximum_idle_size_channel = 1usize.max(maximum_idle_size); + let (tx_idle, rx_idle) = mpsc::channel(maximum_idle_size_channel); + Self { + loopback_networking, + idle_http_sockets: tokio::sync::Mutex::new(rx_idle), + send_socket_idle: Arc::new(tx_idle), + proxy_connect_init_timeout, + proxy_connect_nominal_timeout, + maximum_idle_size, + is_running: AtomicBool::new(false), + } + } + + pub async fn acquire_http_socket(&self, reuse: bool) -> anyhow::Result { + loop { + // We will only reuse the socket connection if its an idempotent as sockets that + // have been closed by the server can not be retried otherwise + let socket = if reuse { + if let Ok(mut guard) = self.idle_http_sockets.try_lock() { + guard.try_recv().ok() + } else { + None + } + } else { + None + }; + + // Check the socket is active if it is not then + // we need to open a new socket + let ret = match socket { + Some(s) if s.is_active() => { + tracing::trace!("reusing socket for proxy handler (R1)"); + Ok(s) + } + Some(_) => { + tracing::trace!("socket in pool is no long active - trying again"); + continue; + } + None => { + // Determine which timeout to use + let connect_timeout = if self.is_running.load(Ordering::SeqCst) == true { + self.proxy_connect_nominal_timeout + } else { + self.proxy_connect_init_timeout + }; + + // Use a guard as without this concurrent connects seem to timeout + // due to a race condition + tracing::trace!("opening new socket for proxy handler"); + if let Some(socket) = self.try_reuse_proxy_http_socket().await? { + Ok(socket) + } else { + tokio::time::timeout(connect_timeout, self.open_proxy_http_socket()).await? + } + } + }; + + match ret.as_ref() { + Err(err) => { + tracing::debug!("connection attempt failed - {}", err); + } + Ok(_) => { + // If we have successfully acquired a HTTP connection then we are + // allowed to taint the instance on future failures + self.is_running.store(true, Ordering::Relaxed) + } + } + + return ret; + } + } + + pub async fn reuse_proxy_http_socket(&self) -> anyhow::Result { + let mut rx_socket = self.idle_http_sockets.lock().await; + let socket = rx_socket.recv().await; + if socket.is_some() { + tracing::trace!("reusing socket for proxy handler (R2)"); + } + socket.ok_or(anyhow::format_err!("proxy has shutdown")) + } + + pub async fn try_reuse_proxy_http_socket(&self) -> anyhow::Result> { + let mut rx_socket = self.idle_http_sockets.lock().await; + let socket = match rx_socket.try_recv() { + Ok(socket) => { + tracing::trace!("reusing socket for proxy handler (R3)"); + Some(socket) + } + Err(TryRecvError::Empty) => None, + Err(TryRecvError::Disconnected) => { + return Err(anyhow::format_err!("proxy has shutdown")); + } + }; + Ok(socket) + } + + pub async fn open_proxy_http_socket(&self) -> anyhow::Result { + let dst = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 80); + + // Open a connection directly to the loopback port + // (or at least try to) + self.loopback_networking + .loopback_connect_to(SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), dst) + .ok_or_else(|| { + tracing::debug!( + "proxy connection attempt failed - could not connect to http server socket as the loopback socket is not open", + ); + anyhow::anyhow!("failed to open HTTP socket as the loopback socket is not open") + }) + } + + pub fn return_http_socket(&self, socket: TcpSocketHalf) { + if self.maximum_idle_size > 0 { + tracing::trace!("reusing active socket"); + match self.send_socket_idle.try_send(socket) { + Err(TrySendError::Closed(socket)) => { + tracing::trace!("closing socket (reuse pipe is closed)"); + socket.close().ok(); + } + Err(TrySendError::Full(socket)) => { + tracing::trace!("closing socket (too many sockets)"); + socket.close().ok(); + } + Ok(_) => {} + } + } else { + tracing::trace!("closing socket (reuse is disabled)"); + socket.close().ok(); + } + } +} diff --git a/lib/wasix/src/runners/mod.rs b/lib/wasix/src/runners/mod.rs index e32221049ec..d4951106d10 100644 --- a/lib/wasix/src/runners/mod.rs +++ b/lib/wasix/src/runners/mod.rs @@ -2,6 +2,8 @@ mod runner; #[cfg(feature = "webc_runner_rt_dcgi")] pub mod dcgi; +#[cfg(feature = "webc_runner_rt_dproxy")] +pub mod dproxy; #[cfg(feature = "webc_runner_rt_emscripten")] pub mod emscripten; pub mod wasi; From 1c646e2cd0c082ebadb560a0a0b87ebb72950395 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Fri, 5 Jan 2024 06:40:52 +1100 Subject: [PATCH 02/67] Simplifications of DProxy --- lib/cli/src/commands/run/mod.rs | 5 +- lib/wasix/src/runners/dproxy/factory.rs | 19 +-- .../src/runners/dproxy/hyper_proxy/builder.rs | 12 +- .../runners/dproxy/hyper_proxy/connector.rs | 9 +- lib/wasix/src/runners/dproxy/instance.rs | 2 + lib/wasix/src/runners/dproxy/runner.rs | 10 +- .../src/runners/dproxy/socket_manager.rs | 127 ++---------------- 7 files changed, 29 insertions(+), 155 deletions(-) diff --git a/lib/cli/src/commands/run/mod.rs b/lib/cli/src/commands/run/mod.rs index 1f306978405..9bba70387a5 100644 --- a/lib/cli/src/commands/run/mod.rs +++ b/lib/cli/src/commands/run/mod.rs @@ -193,7 +193,7 @@ impl Run { if DcgiRunner::can_run_command(cmd.metadata())? { self.run_dcgi(id, pkg, uses, runtime) } else if DProxyRunner::can_run_command(cmd.metadata())? { - self.run_dproxy(id, pkg, uses, runtime) + self.run_dproxy(id, pkg, runtime) } else if WcgiRunner::can_run_command(cmd.metadata())? { self.run_wcgi(id, pkg, uses, runtime) } else if WasiRunner::can_run_command(cmd.metadata())? { @@ -315,11 +315,10 @@ impl Run { &self, command_name: &str, pkg: &BinaryPackage, - uses: Vec, runtime: Arc, ) -> Result<(), Error> { let mut inner = self.build_wasi_runner(&runtime)?; - let mut runner = wasmer_wasix::runners::dproxy::DProxyRunner::new(inner, pkg, uses); + let mut runner = wasmer_wasix::runners::dproxy::DProxyRunner::new(inner, pkg); runner.run_command(command_name, pkg, runtime) } diff --git a/lib/wasix/src/runners/dproxy/factory.rs b/lib/wasix/src/runners/dproxy/factory.rs index 0d4d936c10d..71fbd6ece34 100644 --- a/lib/wasix/src/runners/dproxy/factory.rs +++ b/lib/wasix/src/runners/dproxy/factory.rs @@ -50,11 +50,7 @@ impl DProxyInstanceFactory { } } - pub async fn spin_up( - &self, - handler: &Handler, - _shard: Shard, - ) -> anyhow::Result { + pub async fn spin_up(&self, handler: &Handler, shard: Shard) -> anyhow::Result { // Get the runtime with its already wired local networking let runtime = handler.runtime.clone(); @@ -70,17 +66,16 @@ impl DProxyInstanceFactory { composite_networking.loopback_networking(), handler.config.proxy_connect_init_timeout, handler.config.proxy_connect_nominal_timeout, - handler.config.max_socket_pool_size, )); runtime = runtime.with_networking(Arc::new(composite_networking)); // The connector uses the socket manager to open sockets to the instance let connector = HyperProxyConnectorBuilder::new(socket_manager.clone()) - .with_reuse(true) .build() .await; // Now we run the actual instance under a WasiRunner + let this = self.clone(); let pkg = handler.config.pkg.clone(); let command_name = handler.command_name.clone(); let runtime = Arc::new(runtime) as Arc; @@ -89,8 +84,14 @@ impl DProxyInstanceFactory { .task_manager() .clone() .task_dedicated(Box::new(move || { - runner.run_command(&command_name, &pkg, runtime); - })); + if let Err(err) = runner.run_command(&command_name, &pkg, runtime) { + tracing::error!("Instance Exited: {}", err); + } else { + tracing::info!("Instance Exited: Nominal"); + } + let mut state = this.state.lock().unwrap(); + state.instance.remove(&shard); + }))?; // Return an instance Ok(DProxyInstance { diff --git a/lib/wasix/src/runners/dproxy/hyper_proxy/builder.rs b/lib/wasix/src/runners/dproxy/hyper_proxy/builder.rs index d8f914517f8..00265d228bd 100644 --- a/lib/wasix/src/runners/dproxy/hyper_proxy/builder.rs +++ b/lib/wasix/src/runners/dproxy/hyper_proxy/builder.rs @@ -7,26 +7,16 @@ use super::*; #[derive(Debug)] pub struct HyperProxyConnectorBuilder { socket_manager: Arc, - reuse: bool, } impl HyperProxyConnectorBuilder { pub fn new(socket_manager: Arc) -> Self { - Self { - socket_manager, - reuse: true, - } - } - - pub fn with_reuse(mut self, reuse: bool) -> Self { - self.reuse = reuse; - self + Self { socket_manager } } pub async fn build(self) -> HyperProxyConnector { HyperProxyConnector { socket_manager: self.socket_manager, - reuse: self.reuse, } } } diff --git a/lib/wasix/src/runners/dproxy/hyper_proxy/connector.rs b/lib/wasix/src/runners/dproxy/hyper_proxy/connector.rs index 473dbab1c80..6d2943b6425 100644 --- a/lib/wasix/src/runners/dproxy/hyper_proxy/connector.rs +++ b/lib/wasix/src/runners/dproxy/hyper_proxy/connector.rs @@ -8,13 +8,6 @@ use super::*; #[derive(Debug, Clone)] pub struct HyperProxyConnector { pub(super) socket_manager: Arc, - pub(super) reuse: bool, -} - -impl HyperProxyConnector { - pub fn socket_manager(&self) -> &Arc { - &self.socket_manager - } } impl Service for HyperProxyConnector { @@ -31,7 +24,7 @@ impl Service for HyperProxyConnector { fn call(&mut self, _dst: Uri) -> Self::Future { let this = self.clone(); Box::pin(async move { - let socket = this.socket_manager.acquire_http_socket(this.reuse).await?; + let socket = this.socket_manager.acquire_http_socket().await?; let (tx, rx) = socket.split(); Ok(HyperProxyStream { tx, rx }) }) diff --git a/lib/wasix/src/runners/dproxy/instance.rs b/lib/wasix/src/runners/dproxy/instance.rs index 5b875ed2450..edbb8e656fe 100644 --- a/lib/wasix/src/runners/dproxy/instance.rs +++ b/lib/wasix/src/runners/dproxy/instance.rs @@ -7,7 +7,9 @@ use super::{hyper_proxy::HyperProxyConnector, socket_manager::SocketManager}; #[derive(Debug, Clone)] pub struct DProxyInstance { + #[allow(unused)] pub(super) last_used: Arc>, + #[allow(unused)] pub(super) socket_manager: Arc, pub(super) client: hyper::Client, } diff --git a/lib/wasix/src/runners/dproxy/runner.rs b/lib/wasix/src/runners/dproxy/runner.rs index a0318c5d2b1..6bfa3d9a9ce 100644 --- a/lib/wasix/src/runners/dproxy/runner.rs +++ b/lib/wasix/src/runners/dproxy/runner.rs @@ -23,9 +23,9 @@ pub struct DProxyRunner { } impl DProxyRunner { - pub fn new(inner: WasiRunner, pkg: &BinaryPackage, uses: Vec) -> Self { + pub fn new(inner: WasiRunner, pkg: &BinaryPackage) -> Self { Self { - config: Config::new(inner, pkg, uses), + config: Config::new(inner, pkg), factory: DProxyInstanceFactory::new(), } } @@ -107,22 +107,18 @@ pub struct Config { pub(crate) inner: WasiRunner, pub(crate) addr: SocketAddr, pub(crate) pkg: BinaryPackage, - pub(crate) uses: Vec, pub(crate) proxy_connect_init_timeout: Duration, pub(crate) proxy_connect_nominal_timeout: Duration, - pub(crate) max_socket_pool_size: usize, } impl Config { - pub fn new(inner: WasiRunner, pkg: &BinaryPackage, uses: Vec) -> Self { + pub fn new(inner: WasiRunner, pkg: &BinaryPackage) -> Self { Self { inner, pkg: pkg.clone(), - uses, addr: ([127, 0, 0, 1], 8000).into(), proxy_connect_init_timeout: Duration::from_secs(30), proxy_connect_nominal_timeout: Duration::from_secs(30), - max_socket_pool_size: 50, } } diff --git a/lib/wasix/src/runners/dproxy/socket_manager.rs b/lib/wasix/src/runners/dproxy/socket_manager.rs index cc5d49dde67..5e457a7116b 100644 --- a/lib/wasix/src/runners/dproxy/socket_manager.rs +++ b/lib/wasix/src/runners/dproxy/socket_manager.rs @@ -1,26 +1,16 @@ use std::{ net::{IpAddr, Ipv4Addr, SocketAddr}, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, + sync::atomic::{AtomicBool, Ordering}, time::Duration, }; -use tokio::sync::mpsc::{ - self, - error::{TryRecvError, TrySendError}, -}; use virtual_net::{tcp_pair::TcpSocketHalf, LoopbackNetworking}; #[derive(Debug)] pub struct SocketManager { loopback_networking: LoopbackNetworking, - idle_http_sockets: tokio::sync::Mutex>, - send_socket_idle: Arc>, proxy_connect_init_timeout: Duration, proxy_connect_nominal_timeout: Duration, - maximum_idle_size: usize, is_running: AtomicBool, } @@ -29,102 +19,25 @@ impl SocketManager { loopback_networking: LoopbackNetworking, proxy_connect_init_timeout: Duration, proxy_connect_nominal_timeout: Duration, - maximum_idle_size: usize, ) -> Self { - let maximum_idle_size_channel = 1usize.max(maximum_idle_size); - let (tx_idle, rx_idle) = mpsc::channel(maximum_idle_size_channel); Self { loopback_networking, - idle_http_sockets: tokio::sync::Mutex::new(rx_idle), - send_socket_idle: Arc::new(tx_idle), proxy_connect_init_timeout, proxy_connect_nominal_timeout, - maximum_idle_size, is_running: AtomicBool::new(false), } } - pub async fn acquire_http_socket(&self, reuse: bool) -> anyhow::Result { - loop { - // We will only reuse the socket connection if its an idempotent as sockets that - // have been closed by the server can not be retried otherwise - let socket = if reuse { - if let Ok(mut guard) = self.idle_http_sockets.try_lock() { - guard.try_recv().ok() - } else { - None - } - } else { - None - }; - - // Check the socket is active if it is not then - // we need to open a new socket - let ret = match socket { - Some(s) if s.is_active() => { - tracing::trace!("reusing socket for proxy handler (R1)"); - Ok(s) - } - Some(_) => { - tracing::trace!("socket in pool is no long active - trying again"); - continue; - } - None => { - // Determine which timeout to use - let connect_timeout = if self.is_running.load(Ordering::SeqCst) == true { - self.proxy_connect_nominal_timeout - } else { - self.proxy_connect_init_timeout - }; - - // Use a guard as without this concurrent connects seem to timeout - // due to a race condition - tracing::trace!("opening new socket for proxy handler"); - if let Some(socket) = self.try_reuse_proxy_http_socket().await? { - Ok(socket) - } else { - tokio::time::timeout(connect_timeout, self.open_proxy_http_socket()).await? - } - } - }; - - match ret.as_ref() { - Err(err) => { - tracing::debug!("connection attempt failed - {}", err); - } - Ok(_) => { - // If we have successfully acquired a HTTP connection then we are - // allowed to taint the instance on future failures - self.is_running.store(true, Ordering::Relaxed) - } - } - - return ret; - } - } - - pub async fn reuse_proxy_http_socket(&self) -> anyhow::Result { - let mut rx_socket = self.idle_http_sockets.lock().await; - let socket = rx_socket.recv().await; - if socket.is_some() { - tracing::trace!("reusing socket for proxy handler (R2)"); - } - socket.ok_or(anyhow::format_err!("proxy has shutdown")) - } - - pub async fn try_reuse_proxy_http_socket(&self) -> anyhow::Result> { - let mut rx_socket = self.idle_http_sockets.lock().await; - let socket = match rx_socket.try_recv() { - Ok(socket) => { - tracing::trace!("reusing socket for proxy handler (R3)"); - Some(socket) - } - Err(TryRecvError::Empty) => None, - Err(TryRecvError::Disconnected) => { - return Err(anyhow::format_err!("proxy has shutdown")); - } + pub async fn acquire_http_socket(&self) -> anyhow::Result { + let connect_timeout = if self.is_running.load(Ordering::SeqCst) == true { + self.proxy_connect_nominal_timeout + } else { + self.proxy_connect_init_timeout }; - Ok(socket) + + let ret = tokio::time::timeout(connect_timeout, self.open_proxy_http_socket()).await??; + self.is_running.store(true, Ordering::Relaxed); + Ok(ret) } pub async fn open_proxy_http_socket(&self) -> anyhow::Result { @@ -141,24 +54,4 @@ impl SocketManager { anyhow::anyhow!("failed to open HTTP socket as the loopback socket is not open") }) } - - pub fn return_http_socket(&self, socket: TcpSocketHalf) { - if self.maximum_idle_size > 0 { - tracing::trace!("reusing active socket"); - match self.send_socket_idle.try_send(socket) { - Err(TrySendError::Closed(socket)) => { - tracing::trace!("closing socket (reuse pipe is closed)"); - socket.close().ok(); - } - Err(TrySendError::Full(socket)) => { - tracing::trace!("closing socket (too many sockets)"); - socket.close().ok(); - } - Ok(_) => {} - } - } else { - tracing::trace!("closing socket (reuse is disabled)"); - socket.close().ok(); - } - } } From 09b75566ff46224141ba8dd9f05b9dd2c5d0c1a5 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Fri, 5 Jan 2024 20:16:37 +1100 Subject: [PATCH 03/67] Fixed an issue where the runtime would not load properly in DProxy --- lib/wasix/src/runners/dproxy/factory.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/wasix/src/runners/dproxy/factory.rs b/lib/wasix/src/runners/dproxy/factory.rs index 71fbd6ece34..9d05197af84 100644 --- a/lib/wasix/src/runners/dproxy/factory.rs +++ b/lib/wasix/src/runners/dproxy/factory.rs @@ -75,6 +75,8 @@ impl DProxyInstanceFactory { .await; // Now we run the actual instance under a WasiRunner + #[cfg(feature = "sys")] + let handle = tokio::runtime::Handle::current(); let this = self.clone(); let pkg = handler.config.pkg.clone(); let command_name = handler.command_name.clone(); @@ -84,6 +86,8 @@ impl DProxyInstanceFactory { .task_manager() .clone() .task_dedicated(Box::new(move || { + #[cfg(feature = "sys")] + let _guard = handle.enter(); if let Err(err) = runner.run_command(&command_name, &pkg, runtime) { tracing::error!("Instance Exited: {}", err); } else { From d82b1dc344c85273fc5f66246fcd40f0681c2682 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Sat, 6 Jan 2024 06:38:00 +1100 Subject: [PATCH 04/67] Simplified the loopback listen method so that it fixes a multiplexing issue --- lib/journal/src/concrete/archived.rs | 7 +- lib/journal/src/concrete/archived_from.rs | 14 +-- lib/wasix/src/runners/dproxy/factory.rs | 6 ++ lib/wasix/src/runners/dproxy/networking.rs | 91 ++++++------------- .../src/runners/dproxy/socket_manager.rs | 23 ++++- 5 files changed, 68 insertions(+), 73 deletions(-) diff --git a/lib/journal/src/concrete/archived.rs b/lib/journal/src/concrete/archived.rs index ad0cbf98a88..3f694da12bf 100644 --- a/lib/journal/src/concrete/archived.rs +++ b/lib/journal/src/concrete/archived.rs @@ -741,7 +741,11 @@ impl<'a> JournalEntry<'a> { serializer.serialize_value(&JournalEntrySocketListenV1 { fd, backlog }) } JournalEntry::SocketBindV1 { fd, addr } => { - serializer.serialize_value(&JournalEntrySocketBindV1 { fd, addr }) + serializer.serialize_value(&JournalEntrySocketBindV1 { + fd, + addr, + _padding: 0, + }) } JournalEntry::SocketConnectedV1 { fd, addr } => { serializer.serialize_value(&JournalEntrySocketConnectedV1 { fd, addr }) @@ -1355,6 +1359,7 @@ pub struct JournalEntrySocketListenV1 { #[archive_attr(derive(CheckBytes))] pub struct JournalEntrySocketBindV1 { pub fd: u32, + pub _padding: u32, pub addr: SocketAddr, } diff --git a/lib/journal/src/concrete/archived_from.rs b/lib/journal/src/concrete/archived_from.rs index b37e53f3c26..5b553793473 100644 --- a/lib/journal/src/concrete/archived_from.rs +++ b/lib/journal/src/concrete/archived_from.rs @@ -952,12 +952,14 @@ impl<'a> TryFrom> for JournalEntry<'a> { fd: *fd, backlog: *backlog, }, - ArchivedJournalEntry::SocketBindV1(ArchivedJournalEntrySocketBindV1 { fd, addr }) => { - Self::SocketBindV1 { - fd: *fd, - addr: addr.as_socket_addr(), - } - } + ArchivedJournalEntry::SocketBindV1(ArchivedJournalEntrySocketBindV1 { + fd, + addr, + _padding, + }) => Self::SocketBindV1 { + fd: *fd, + addr: addr.as_socket_addr(), + }, ArchivedJournalEntry::SocketConnectedV1(ArchivedJournalEntrySocketConnectedV1 { fd, addr, diff --git a/lib/wasix/src/runners/dproxy/factory.rs b/lib/wasix/src/runners/dproxy/factory.rs index 9d05197af84..042bdd25c3c 100644 --- a/lib/wasix/src/runners/dproxy/factory.rs +++ b/lib/wasix/src/runners/dproxy/factory.rs @@ -1,6 +1,7 @@ use std::{ collections::HashMap, sync::{Arc, Mutex}, + task::Context, time::Instant, }; @@ -62,7 +63,12 @@ impl DProxyInstanceFactory { // We attach a composite networking to the runtime which includes a loopback // networking implementation connected to a socket manager let composite_networking = LocalWithLoopbackNetworking::new(); + let poll_listening = { + let networking = composite_networking.clone(); + Arc::new(move |cx: &mut Context<'_>| networking.poll_listening(cx)) + }; let socket_manager = Arc::new(SocketManager::new( + poll_listening, composite_networking.loopback_networking(), handler.config.proxy_connect_init_timeout, handler.config.proxy_connect_nominal_timeout, diff --git a/lib/wasix/src/runners/dproxy/networking.rs b/lib/wasix/src/runners/dproxy/networking.rs index fd78f6eba9c..ff3d56b8b74 100644 --- a/lib/wasix/src/runners/dproxy/networking.rs +++ b/lib/wasix/src/runners/dproxy/networking.rs @@ -1,14 +1,14 @@ use std::{ net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, sync::{Arc, Mutex}, - task::Waker, + task::{Context, Poll, Waker}, time::Duration, }; use virtual_net::{ - host::LocalNetworking, loopback::LoopbackNetworking, CompositeTcpListener, IpCidr, IpRoute, - NetworkError, StreamSecurity, VirtualIcmpSocket, VirtualNetworking, VirtualRawSocket, - VirtualTcpListener, VirtualTcpSocket, VirtualUdpSocket, + host::LocalNetworking, loopback::LoopbackNetworking, IpCidr, IpRoute, NetworkError, + StreamSecurity, VirtualIcmpSocket, VirtualNetworking, VirtualRawSocket, VirtualTcpListener, + VirtualTcpSocket, VirtualUdpSocket, }; #[derive(Debug, Default)] @@ -17,10 +17,9 @@ struct LocalWithLoopbackNetworkingListening { wakers: Vec, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct LocalWithLoopbackNetworking { inner_networking: Arc, - listen_port: Option, local_listening: Arc>, loopback_networking: LoopbackNetworking, } @@ -32,12 +31,25 @@ impl LocalWithLoopbackNetworking { } Self { local_listening: Default::default(), - listen_port: None, inner_networking: LOCAL_NETWORKING.clone(), loopback_networking: LoopbackNetworking::new(), } } + pub fn poll_listening(&self, cx: &mut Context<'_>) -> Poll { + let mut listening = self.local_listening.lock().unwrap(); + + if let Some(addr) = listening.addresses.first() { + return Poll::Ready(*addr); + } + + if listening.wakers.iter().any(|w| w.will_wake(cx.waker())) == false { + listening.wakers.push(cx.waker().clone()); + } + + Poll::Pending + } + pub fn register_listener(&self, addr: SocketAddr) { let mut listening = self.local_listening.lock().unwrap(); listening.addresses.push(addr); @@ -150,42 +162,17 @@ impl VirtualNetworking for LocalWithLoopbackNetworking { reuse_port: bool, reuse_addr: bool, ) -> Result, NetworkError> { - // We determine if the listener should be registered so that - // anyone waiting to start a connection attempt from the proxy - // can do so - let add_listener = if let Some(special_port) = self.listen_port { - addr.port() == special_port - } else { - true - }; - let backlog = 1024; - let ret: Result, NetworkError> = - if is_ip_unspecified(&addr.ip()) { - let mut socket = CompositeTcpListener::new(); - socket.add_port( - self.loopback_networking - .listen_tcp(addr, only_v6, reuse_port, reuse_addr) - .await?, - ); - socket.add_port( - self.inner_networking - .listen_tcp(addr, only_v6, reuse_port, reuse_addr) - .await?, - ); - Ok(Box::new(socket)) - } else if is_ip_loopback(&addr.ip()) { - self.loopback_networking - .listen_tcp(addr, only_v6, reuse_port, reuse_addr) - .await - } else { - self.inner_networking - .listen_tcp(addr, only_v6, reuse_port, reuse_addr) - .await - }; - - if add_listener { + tracing::debug!("registering listener on loopback networking"); + + let ret: Result, NetworkError> = self + .loopback_networking + .listen_tcp(addr, only_v6, reuse_port, reuse_addr) + .await; + + if ret.is_ok() { + tracing::debug!("registering listener on loopback networking"); self.register_listener(addr); } @@ -234,25 +221,3 @@ impl VirtualNetworking for LocalWithLoopbackNetworking { self.inner_networking.resolve(host, port, dns_server).await } } - -pub const fn is_ip_unspecified(ip: &IpAddr) -> bool { - match ip { - IpAddr::V4(ip) => ip.is_unspecified(), - IpAddr::V6(ip) => ip.is_unspecified(), - } -} - -pub const fn is_ip_loopback(ip: &IpAddr) -> bool { - match ip { - IpAddr::V4(ip) => is_ip4_loopback(ip), - IpAddr::V6(ip) => is_ip6_loopback(ip), - } -} - -pub const fn is_ip4_loopback(ip: &Ipv4Addr) -> bool { - ip.is_loopback() -} - -pub const fn is_ip6_loopback(ip: &Ipv6Addr) -> bool { - ip.is_loopback() -} diff --git a/lib/wasix/src/runners/dproxy/socket_manager.rs b/lib/wasix/src/runners/dproxy/socket_manager.rs index 5e457a7116b..21aeb20e44b 100644 --- a/lib/wasix/src/runners/dproxy/socket_manager.rs +++ b/lib/wasix/src/runners/dproxy/socket_manager.rs @@ -1,13 +1,25 @@ use std::{ + future::poll_fn, net::{IpAddr, Ipv4Addr, SocketAddr}, - sync::atomic::{AtomicBool, Ordering}, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + task::{Context, Poll}, time::Duration, }; +use derivative::Derivative; use virtual_net::{tcp_pair::TcpSocketHalf, LoopbackNetworking}; -#[derive(Debug)] +pub type PollListeningFn = + Arc) -> Poll + Send + Sync + 'static>; + +#[derive(Derivative)] +#[derivative(Debug)] pub struct SocketManager { + #[derivative(Debug = "ignore")] + poll_listening: PollListeningFn, loopback_networking: LoopbackNetworking, proxy_connect_init_timeout: Duration, proxy_connect_nominal_timeout: Duration, @@ -16,11 +28,13 @@ pub struct SocketManager { impl SocketManager { pub fn new( + poll_listening: PollListeningFn, loopback_networking: LoopbackNetworking, proxy_connect_init_timeout: Duration, proxy_connect_nominal_timeout: Duration, ) -> Self { Self { + poll_listening, loopback_networking, proxy_connect_init_timeout, proxy_connect_nominal_timeout, @@ -41,7 +55,10 @@ impl SocketManager { } pub async fn open_proxy_http_socket(&self) -> anyhow::Result { - let dst = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 80); + // We need to find the destination address + let poll_listening = self.poll_listening.clone(); + let port = poll_fn(|cx| poll_listening(cx)).await.port(); + let dst = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port); // Open a connection directly to the loopback port // (or at least try to) From 92ef764399759ede398fad8655ebc0a5f35a1ed0 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 8 Jan 2024 08:19:49 +1100 Subject: [PATCH 05/67] Refactoring of the rewind handling for DProxy --- lib/wasix/src/os/task/thread.rs | 8 +++--- lib/wasix/src/runners/dproxy/networking.rs | 2 +- lib/wasix/src/syscalls/journal.rs | 6 ++++- lib/wasix/src/syscalls/mod.rs | 31 +++++++++++++++------- 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/lib/wasix/src/os/task/thread.rs b/lib/wasix/src/os/task/thread.rs index 94ee92a4ec4..ffcc091ec21 100644 --- a/lib/wasix/src/os/task/thread.rs +++ b/lib/wasix/src/os/task/thread.rs @@ -116,13 +116,15 @@ impl WasiThread { self.rewind.take() } - pub(crate) fn has_rewind_of_type(&self, _type: HandleRewindType) -> bool { - match _type { + /// Returns true if a rewind of a particular type has been queued + /// for processed by a rewind operation + pub(crate) fn has_rewind_of_type(&self, type_: HandleRewindType) -> bool { + match type_ { HandleRewindType::ResultDriven => match &self.rewind { Some(rewind) => rewind.rewind_result.is_some(), None => false, }, - HandleRewindType::Resultless => match &self.rewind { + HandleRewindType::ResultLess => match &self.rewind { Some(rewind) => rewind.rewind_result.is_none(), None => false, }, diff --git a/lib/wasix/src/runners/dproxy/networking.rs b/lib/wasix/src/runners/dproxy/networking.rs index ff3d56b8b74..9e7baf2915a 100644 --- a/lib/wasix/src/runners/dproxy/networking.rs +++ b/lib/wasix/src/runners/dproxy/networking.rs @@ -1,5 +1,5 @@ use std::{ - net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, + net::{IpAddr, SocketAddr}, sync::{Arc, Mutex}, task::{Context, Poll, Waker}, time::Duration, diff --git a/lib/wasix/src/syscalls/journal.rs b/lib/wasix/src/syscalls/journal.rs index c2040111291..6084d54dd80 100644 --- a/lib/wasix/src/syscalls/journal.rs +++ b/lib/wasix/src/syscalls/journal.rs @@ -16,7 +16,11 @@ pub fn maybe_snapshot_once( ) -> WasiResult> { use crate::os::task::process::{WasiProcessCheckpoint, WasiProcessInner}; - unsafe { handle_rewind_ext::(&mut ctx, HandleRewindType::Resultless) }; + if unsafe { handle_rewind_ext_with_default::(&mut ctx, HandleRewindType::ResultLess) } + .is_some() + { + return Ok(Ok(ctx)); + } if !ctx.data().enable_journal { return Ok(Ok(ctx)); diff --git a/lib/wasix/src/syscalls/mod.rs b/lib/wasix/src/syscalls/mod.rs index 610f4075419..e3ef3ac7b1e 100644 --- a/lib/wasix/src/syscalls/mod.rs +++ b/lib/wasix/src/syscalls/mod.rs @@ -1264,25 +1264,36 @@ pub(crate) unsafe fn handle_rewind( where T: serde::de::DeserializeOwned, { - handle_rewind_ext::(ctx, HandleRewindType::ResultDriven) + handle_rewind_ext::(ctx, HandleRewindType::ResultDriven).flatten() } pub(crate) enum HandleRewindType { /// Handle rewind types that have a result to be processed ResultDriven, - /// Handle rewind types that are resultless (generally these + /// Handle rewind types that are result-less (generally these /// are caused by snapshot events) - Resultless, + ResultLess, } -pub(crate) unsafe fn handle_rewind_ext( +pub(crate) unsafe fn handle_rewind_ext_with_default( ctx: &mut FunctionEnvMut<'_, WasiEnv>, - _type: HandleRewindType, + type_: HandleRewindType, ) -> Option +where + T: serde::de::DeserializeOwned + Default, +{ + let ret = handle_rewind_ext::(ctx, type_); + ret.unwrap_or_default() +} + +pub(crate) unsafe fn handle_rewind_ext( + ctx: &mut FunctionEnvMut<'_, WasiEnv>, + type_: HandleRewindType, +) -> Option> where T: serde::de::DeserializeOwned, { - if !ctx.data().thread.has_rewind_of_type(_type) { + if !ctx.data().thread.has_rewind_of_type(type_) { return None; }; @@ -1297,7 +1308,7 @@ where asyncify_stop_rewind.call(ctx); } else { warn!("failed to handle rewind because the asyncify_start_rewind export is missing or inaccessible"); - return None; + return Some(None); } // Restore the memory stack @@ -1307,12 +1318,12 @@ where if let Some(rewind_result) = result.rewind_result { let ret = bincode::deserialize(&rewind_result) .expect("failed to deserialize the rewind result"); - Some(ret) + Some(Some(ret)) } else { - None + Some(None) } } else { - None + Some(None) } } From 4f2e4a6ab5e24951ef982dd13281d01a5b18cad7 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 8 Jan 2024 21:42:21 +1100 Subject: [PATCH 06/67] Fixed an issue where the caching compiled modules were not saving properly --- .../src/runtime/module_cache/filesystem.rs | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/wasix/src/runtime/module_cache/filesystem.rs b/lib/wasix/src/runtime/module_cache/filesystem.rs index a2e60f549bc..30db6f78451 100644 --- a/lib/wasix/src/runtime/module_cache/filesystem.rs +++ b/lib/wasix/src/runtime/module_cache/filesystem.rs @@ -63,19 +63,19 @@ impl ModuleCache for FileSystemCache { } Err(e) => { tracing::debug!( - %key, - path=%path.display(), - error=&e as &dyn std::error::Error, - "Deleting the cache file because the artifact couldn't be deserialized", - ); + %key, + path=%path.display(), + error=&e as &dyn std::error::Error, + "Deleting the cache file because the artifact couldn't be deserialized", + ); if let Err(e) = std::fs::remove_file(&path) { tracing::warn!( - %key, - path=%path.display(), - error=&e as &dyn std::error::Error, - "Unable to remove the corrupted cache file", - ); + %key, + path=%path.display(), + error=&e as &dyn std::error::Error, + "Unable to remove the corrupted cache file", + ); } Err(e) @@ -131,10 +131,11 @@ impl ModuleCache for FileSystemCache { .await .unwrap()?; - if let Err(error) = tokio::io::BufWriter::new(&mut file) - .write_all(&serialized) - .await - { + let mut writer = tokio::io::BufWriter::new(&mut file); + if let Err(error) = writer.write_all(&serialized).await { + return Err(CacheError::FileWrite { path, error }); + } + if let Err(error) = writer.flush().await { return Err(CacheError::FileWrite { path, error }); } From fbfbcef4141392bf7cec9e0879e174d585f0d42a Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Tue, 9 Jan 2024 19:53:44 +1100 Subject: [PATCH 07/67] Some minor journal optimizations for a restored state --- lib/wasix/src/state/func_env.rs | 37 ++++++++++++++++++------------- lib/wasix/src/syscalls/journal.rs | 4 ---- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/lib/wasix/src/state/func_env.rs b/lib/wasix/src/state/func_env.rs index cff6ed1918a..1de004467d9 100644 --- a/lib/wasix/src/state/func_env.rs +++ b/lib/wasix/src/state/func_env.rs @@ -275,21 +275,28 @@ impl WasiFunctionEnv { self.data_mut(&mut store).replaying_journal = false; } - // The first event we save is an event that records the module hash. - // Note: This is used to detect if an incorrect journal is used on the wrong - // process or if a process has been recompiled - let wasm_hash = self.data(&store).process.module_hash.as_bytes(); - let mut ctx = self.env.clone().into_mut(&mut store); - crate::journal::JournalEffector::save_event( - &mut ctx, - crate::journal::JournalEntry::InitModuleV1 { wasm_hash }, - ) - .map_err(|err| { - WasiRuntimeError::Runtime(wasmer::RuntimeError::new(format!( - "journal failied to save the module initialization event - {}", - err - ))) - })?; + // If there is no rewind state then the journal is being replayed + // and hence we do not need to write an init module event + // + // But otherwise we need to notify the journal of the module hash + // so that recompiled modules will restart + if rewind_state.is_none() { + // The first event we save is an event that records the module hash. + // Note: This is used to detect if an incorrect journal is used on the wrong + // process or if a process has been recompiled + let wasm_hash = self.data(&store).process.module_hash.as_bytes(); + let mut ctx = self.env.clone().into_mut(&mut store); + crate::journal::JournalEffector::save_event( + &mut ctx, + crate::journal::JournalEntry::InitModuleV1 { wasm_hash }, + ) + .map_err(|err| { + WasiRuntimeError::Runtime(wasmer::RuntimeError::new(format!( + "journal failied to save the module initialization event - {}", + err + ))) + })?; + } } Ok(rewind_state) diff --git a/lib/wasix/src/syscalls/journal.rs b/lib/wasix/src/syscalls/journal.rs index 6084d54dd80..0a6e021f6e3 100644 --- a/lib/wasix/src/syscalls/journal.rs +++ b/lib/wasix/src/syscalls/journal.rs @@ -621,10 +621,6 @@ pub unsafe fn restore_snapshot( JournalEffector::apply_process_exit(&mut ctx, None) .map_err(anyhow_err_to_runtime_err)?; } - } else { - tracing::debug!( - "journal used on a different module - the process will simulate a restart." - ); } // We do not yet support multi threading From 13203fde45b24c533be0b67a4bac3ad6b8f60096 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Tue, 9 Jan 2024 21:21:41 +1100 Subject: [PATCH 08/67] Renamed InstanceSnapshot to StoreSnapshot which better reflects its purpose --- lib/wasix/src/lib.rs | 2 +- lib/wasix/src/os/task/process.rs | 7 +++---- lib/wasix/src/runtime/task_manager/mod.rs | 14 +++++++------- lib/wasix/src/runtime/task_manager/tokio.rs | 2 +- lib/wasix/src/state/func_env.rs | 11 +++++------ lib/wasix/src/syscalls/mod.rs | 8 ++++---- lib/wasix/src/syscalls/wasix/proc_fork.rs | 13 ++++++------- lib/wasix/src/syscalls/wasix/stack_checkpoint.rs | 2 +- lib/wasix/src/syscalls/wasix/thread_spawn.rs | 6 +++--- lib/wasix/src/utils/store.rs | 10 +++++----- 10 files changed, 36 insertions(+), 39 deletions(-) diff --git a/lib/wasix/src/lib.rs b/lib/wasix/src/lib.rs index e039ea7b0bd..9baa626a994 100644 --- a/lib/wasix/src/lib.rs +++ b/lib/wasix/src/lib.rs @@ -106,7 +106,7 @@ pub use crate::{ utils::is_wasix_module, utils::{ get_wasi_version, get_wasi_versions, is_wasi_module, - store::{capture_instance_snapshot, restore_instance_snapshot, InstanceSnapshot}, + store::{capture_store_snapshot, restore_store_snapshot, StoreSnapshot}, WasiVersion, }, }; diff --git a/lib/wasix/src/os/task/process.rs b/lib/wasix/src/os/task/process.rs index d4501248f0e..d6cb91ec181 100644 --- a/lib/wasix/src/os/task/process.rs +++ b/lib/wasix/src/os/task/process.rs @@ -183,10 +183,9 @@ impl WasiProcessInner { // Perform the unwind action unwind::(ctx, move |mut ctx, memory_stack, rewind_stack| { // Grab all the globals and serialize them - let store_data = - crate::utils::store::capture_instance_snapshot(&mut ctx.as_store_mut()) - .serialize() - .unwrap(); + let store_data = crate::utils::store::capture_store_snapshot(&mut ctx.as_store_mut()) + .serialize() + .unwrap(); let memory_stack = memory_stack.freeze(); let rewind_stack = rewind_stack.freeze(); let store_data = Bytes::from(store_data); diff --git a/lib/wasix/src/runtime/task_manager/mod.rs b/lib/wasix/src/runtime/task_manager/mod.rs index b42be6784aa..a0de8d44c49 100644 --- a/lib/wasix/src/runtime/task_manager/mod.rs +++ b/lib/wasix/src/runtime/task_manager/mod.rs @@ -15,7 +15,7 @@ use wasmer_wasix_types::wasi::{Errno, ExitCode}; use crate::os::task::thread::WasiThreadError; use crate::syscalls::AsyncifyFuture; -use crate::{capture_instance_snapshot, InstanceSnapshot, WasiEnv, WasiFunctionEnv, WasiThread}; +use crate::{capture_store_snapshot, StoreSnapshot, WasiEnv, WasiFunctionEnv, WasiThread}; pub use virtual_mio::waker::*; @@ -76,7 +76,7 @@ pub struct TaskWasm<'a, 'b> { pub recycle: Option>, pub env: WasiEnv, pub module: Module, - pub snapshot: Option<&'b InstanceSnapshot>, + pub globals: Option<&'b StoreSnapshot>, pub spawn_type: SpawnMemoryType<'a>, pub trigger: Option>, pub update_layout: bool, @@ -89,7 +89,7 @@ impl<'a, 'b> TaskWasm<'a, 'b> { run, env, module, - snapshot: None, + globals: None, spawn_type: match shared_memory { Some(ty) => SpawnMemoryType::CreateMemoryOfType(ty), None => SpawnMemoryType::CreateMemory, @@ -112,8 +112,8 @@ impl<'a, 'b> TaskWasm<'a, 'b> { self } - pub fn with_snapshot(mut self, snapshot: &'b InstanceSnapshot) -> Self { - self.snapshot.replace(snapshot); + pub fn with_globals(mut self, snapshot: &'b StoreSnapshot) -> Self { + self.globals.replace(snapshot); self } @@ -346,7 +346,7 @@ impl dyn VirtualTaskManager { } } - let snapshot = capture_instance_snapshot(&mut store.as_store_mut()); + let snapshot = capture_store_snapshot(&mut store.as_store_mut()); let env = ctx.data(&store); let module = env.inner().module_clone(); let memory = env.inner().memory_clone(); @@ -374,7 +374,7 @@ impl dyn VirtualTaskManager { false, ) .with_memory(SpawnMemoryType::ShareMemory(memory, store.as_store_ref())) - .with_snapshot(&snapshot) + .with_globals(&snapshot) .with_trigger(Box::new(move || { Box::pin(async move { let mut poller = AsyncifyPollerOwned { diff --git a/lib/wasix/src/runtime/task_manager/tokio.rs b/lib/wasix/src/runtime/task_manager/tokio.rs index 9f6b6752d64..deaa7006945 100644 --- a/lib/wasix/src/runtime/task_manager/tokio.rs +++ b/lib/wasix/src/runtime/task_manager/tokio.rs @@ -151,7 +151,7 @@ impl VirtualTaskManager for TokioTaskManager { let (ctx, store) = WasiFunctionEnv::new_with_store( task.module, task.env, - task.snapshot, + task.globals, task.spawn_type, task.update_layout, )?; diff --git a/lib/wasix/src/state/func_env.rs b/lib/wasix/src/state/func_env.rs index 1de004467d9..8cef5260ffb 100644 --- a/lib/wasix/src/state/func_env.rs +++ b/lib/wasix/src/state/func_env.rs @@ -12,8 +12,8 @@ use crate::{ import_object_for_all_wasi_versions, runtime::SpawnMemoryType, state::WasiInstanceHandles, - utils::{get_wasi_version, get_wasi_versions, store::restore_instance_snapshot}, - InstanceSnapshot, RewindStateOption, WasiEnv, WasiError, WasiRuntimeError, WasiThreadError, + utils::{get_wasi_version, get_wasi_versions, store::restore_store_snapshot}, + RewindStateOption, StoreSnapshot, WasiEnv, WasiError, WasiRuntimeError, WasiThreadError, }; /// The default stack size for WASIX - the number itself is the default that compilers @@ -39,7 +39,7 @@ impl WasiFunctionEnv { pub fn new_with_store( module: Module, env: WasiEnv, - snapshot: Option<&InstanceSnapshot>, + store_snapshot: Option<&StoreSnapshot>, spawn_type: SpawnMemoryType, update_layout: bool, ) -> Result<(Self, Store), WasiThreadError> { @@ -76,9 +76,8 @@ impl WasiFunctionEnv { })?; // Set all the globals - if let Some(snapshot) = snapshot { - tracing::trace!("restoring snapshot for new thread"); - restore_instance_snapshot(&mut store, snapshot); + if let Some(snapshot) = store_snapshot { + restore_store_snapshot(&mut store, snapshot); } Ok((ctx, store)) diff --git a/lib/wasix/src/syscalls/mod.rs b/lib/wasix/src/syscalls/mod.rs index e3ef3ac7b1e..94ce49ee40e 100644 --- a/lib/wasix/src/syscalls/mod.rs +++ b/lib/wasix/src/syscalls/mod.rs @@ -122,7 +122,7 @@ use crate::{ journal::{DynJournal, JournalEffector}, os::task::{process::MaybeCheckpointResult, thread::RewindResult}, runtime::task_manager::InlineWaker, - utils::store::InstanceSnapshot, + utils::store::StoreSnapshot, DeepSleepWork, RewindPostProcess, RewindState, RewindStateOption, SpawnError, WasiInodes, WasiResult, WasiRuntimeError, }; @@ -953,7 +953,7 @@ pub(crate) fn deep_sleep( trigger: Pin>, ) -> Result<(), WasiError> { // Grab all the globals and serialize them - let store_data = crate::utils::store::capture_instance_snapshot(&mut ctx.as_store_mut()) + let store_data = crate::utils::store::capture_store_snapshot(&mut ctx.as_store_mut()) .serialize() .unwrap(); let store_data = Bytes::from(store_data); @@ -1149,14 +1149,14 @@ pub fn rewind_ext( }); // Deserialize the store data back into a snapshot - let store_snapshot = match InstanceSnapshot::deserialize(&store_data[..]) { + let store_snapshot = match StoreSnapshot::deserialize(&store_data[..]) { Ok(a) => a, Err(err) => { warn!("snapshot restore failed - the store snapshot could not be deserialized"); return Errno::Unknown; } }; - crate::utils::store::restore_instance_snapshot(ctx, &store_snapshot); + crate::utils::store::restore_store_snapshot(ctx, &store_snapshot); let env = ctx.data(); let memory = match env.try_memory_view(&ctx) { Some(v) => v, diff --git a/lib/wasix/src/syscalls/wasix/proc_fork.rs b/lib/wasix/src/syscalls/wasix/proc_fork.rs index 9bcdc337312..8a8b627711c 100644 --- a/lib/wasix/src/syscalls/wasix/proc_fork.rs +++ b/lib/wasix/src/syscalls/wasix/proc_fork.rs @@ -1,6 +1,6 @@ use super::*; use crate::{ - capture_instance_snapshot, + capture_store_snapshot, os::task::OwnedTaskStatus, runtime::task_manager::{TaskWasm, TaskWasmRunProperties}, syscalls::*, @@ -84,10 +84,9 @@ pub fn proc_fork( // Perform the unwind action return unwind::(ctx, move |mut ctx, mut memory_stack, rewind_stack| { // Grab all the globals and serialize them - let store_data = - crate::utils::store::capture_instance_snapshot(&mut ctx.as_store_mut()) - .serialize() - .unwrap(); + let store_data = crate::utils::store::capture_store_snapshot(&mut ctx.as_store_mut()) + .serialize() + .unwrap(); let store_data = Bytes::from(store_data); // We first fork the environment and replace the current environment @@ -130,7 +129,7 @@ pub fn proc_fork( let bin_factory = env.bin_factory.clone(); // Perform the unwind action - let snapshot = capture_instance_snapshot(&mut ctx.as_store_mut()); + let snapshot = capture_store_snapshot(&mut ctx.as_store_mut()); unwind::(ctx, move |mut ctx, mut memory_stack, rewind_stack| { let tasks = ctx.data().tasks().clone(); let span = debug_span!( @@ -201,7 +200,7 @@ pub fn proc_fork( tasks_outer .task_wasm( TaskWasm::new(Box::new(run), child_env, module, false) - .with_snapshot(&snapshot) + .with_globals(&snapshot) .with_memory(spawn_type), ) .map_err(|err| { diff --git a/lib/wasix/src/syscalls/wasix/stack_checkpoint.rs b/lib/wasix/src/syscalls/wasix/stack_checkpoint.rs index 84fefad10ba..7100ce79c13 100644 --- a/lib/wasix/src/syscalls/wasix/stack_checkpoint.rs +++ b/lib/wasix/src/syscalls/wasix/stack_checkpoint.rs @@ -45,7 +45,7 @@ pub fn stack_checkpoint( // Perform the unwind action unwind::(ctx, move |mut ctx, mut memory_stack, rewind_stack| { // Grab all the globals and serialize them - let store_data = crate::utils::store::capture_instance_snapshot(&mut ctx.as_store_mut()) + let store_data = crate::utils::store::capture_store_snapshot(&mut ctx.as_store_mut()) .serialize() .unwrap(); let env = ctx.data(); diff --git a/lib/wasix/src/syscalls/wasix/thread_spawn.rs b/lib/wasix/src/syscalls/wasix/thread_spawn.rs index eb6ffa382c2..ead6a31865e 100644 --- a/lib/wasix/src/syscalls/wasix/thread_spawn.rs +++ b/lib/wasix/src/syscalls/wasix/thread_spawn.rs @@ -4,7 +4,7 @@ use super::*; #[cfg(feature = "journal")] use crate::journal::JournalEffector; use crate::{ - capture_instance_snapshot, + capture_store_snapshot, os::task::thread::WasiMemoryLayout, runtime::task_manager::{TaskWasm, TaskWasmRunProperties}, syscalls::*, @@ -121,7 +121,7 @@ pub(crate) fn thread_spawn_internal( return Err(Errno::Notcapable); } let thread_module = unsafe { env.inner() }.module_clone(); - let snapshot = capture_instance_snapshot(&mut ctx.as_store_mut()); + let globals = capture_store_snapshot(&mut ctx.as_store_mut()); let spawn_type = crate::runtime::SpawnMemoryType::ShareMemory(thread_memory, ctx.as_store_ref()); @@ -133,7 +133,7 @@ pub(crate) fn thread_spawn_internal( tasks .task_wasm( TaskWasm::new(Box::new(run), thread_env, thread_module, false) - .with_snapshot(&snapshot) + .with_globals(&globals) .with_memory(spawn_type), ) .map_err(Into::::into)?; diff --git a/lib/wasix/src/utils/store.rs b/lib/wasix/src/utils/store.rs index 1fac581ba90..fdfb6ae2fb1 100644 --- a/lib/wasix/src/utils/store.rs +++ b/lib/wasix/src/utils/store.rs @@ -1,11 +1,11 @@ /// A snapshot that captures the runtime state of an instance. #[derive(Default, serde::Serialize, serde::Deserialize, Clone, Debug)] -pub struct InstanceSnapshot { +pub struct StoreSnapshot { /// Values of all globals, indexed by the same index used in Webassembly. pub globals: Vec, } -impl InstanceSnapshot { +impl StoreSnapshot { pub fn serialize(&self) -> Result, bincode::Error> { bincode::serialize(self) } @@ -15,13 +15,13 @@ impl InstanceSnapshot { } } -pub fn capture_instance_snapshot(store: &mut impl wasmer::AsStoreMut) -> InstanceSnapshot { +pub fn capture_store_snapshot(store: &mut impl wasmer::AsStoreMut) -> StoreSnapshot { let objs = store.objects_mut(); let globals = objs.as_u128_globals(); - InstanceSnapshot { globals } + StoreSnapshot { globals } } -pub fn restore_instance_snapshot(store: &mut impl wasmer::AsStoreMut, snapshot: &InstanceSnapshot) { +pub fn restore_store_snapshot(store: &mut impl wasmer::AsStoreMut, snapshot: &StoreSnapshot) { let objs = store.objects_mut(); for (index, value) in snapshot.globals.iter().enumerate() { From bb1b647e5fec155609e6b889fa15894611521fb2 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Wed, 10 Jan 2024 11:04:10 +1100 Subject: [PATCH 09/67] No longer partially restoring orphaned snapshots --- lib/wasix/src/syscalls/journal.rs | 49 ++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/lib/wasix/src/syscalls/journal.rs b/lib/wasix/src/syscalls/journal.rs index 0a6e021f6e3..c904ed4c91e 100644 --- a/lib/wasix/src/syscalls/journal.rs +++ b/lib/wasix/src/syscalls/journal.rs @@ -90,11 +90,14 @@ pub unsafe fn restore_snapshot( // possible that the threads will be cancelled before all the // events finished the streaming process let mut spawn_threads: HashMap = Default::default(); + let mut staging_spawn_threads: HashMap = Default::default(); + let mut staging_kill_thread: HashSet = Default::default(); // We delay the memory updates until the end as its possible the // memory will be cleared before all the events finished the // streaming process let mut update_memory: HashMap, Cow<'_, [u8]>> = Default::default(); + let mut staging_update_memory: HashMap, Cow<'_, [u8]>> = Default::default(); let mut update_tty = None; // We capture the stdout and stderr while we replay @@ -109,6 +112,7 @@ pub unsafe fn restore_snapshot( let cur_module_hash = Some(ctx.data().process.module_hash.as_bytes()); let mut journal_module_hash = None; let mut rewind = None; + let mut staging_rewind = None; while let Some(next) = journal.read().map_err(anyhow_err_to_runtime_err)? { tracing::trace!("Restoring snapshot event - {next:?}"); match next { @@ -118,8 +122,11 @@ pub unsafe fn restore_snapshot( crate::journal::JournalEntry::ProcessExitV1 { exit_code } => { if bootstrapping { rewind = None; + staging_rewind = None; spawn_threads.clear(); + staging_spawn_threads.clear(); update_memory.clear(); + staging_update_memory.clear(); update_tty.take(); stdout.clear(); stderr.clear(); @@ -164,7 +171,7 @@ pub unsafe fn restore_snapshot( } if bootstrapping { - update_memory.insert(region, data.clone()); + staging_update_memory.insert(region, data.clone()); } else { JournalEffector::apply_memory(&mut ctx, region, &data) .map_err(anyhow_err_to_runtime_err)?; @@ -174,8 +181,11 @@ pub unsafe fn restore_snapshot( if id == ctx.data().tid().raw() { if bootstrapping { rewind = None; + staging_rewind = None; spawn_threads.clear(); + staging_spawn_threads.clear(); update_memory.clear(); + staging_update_memory.clear(); update_tty.take(); stdout.clear(); stderr.clear(); @@ -188,7 +198,8 @@ pub unsafe fn restore_snapshot( .map_err(anyhow_err_to_runtime_err)?; } } else if bootstrapping { - spawn_threads.remove(&Into::::into(id)); + staging_spawn_threads.remove(&Into::::into(id)); + staging_kill_thread.insert(id.into()); } else { JournalEffector::apply_thread_exit( &mut ctx, @@ -218,9 +229,10 @@ pub unsafe fn restore_snapshot( let id = Into::::into(id); if id == ctx.data().tid() { - rewind.replace(state); + staging_rewind.replace(state); } else if bootstrapping { - spawn_threads.insert(id, state); + staging_kill_thread.remove(&id); + staging_spawn_threads.insert(id, state); } else { return Err(WasiRuntimeError::Runtime(RuntimeError::user( anyhow::format_err!( @@ -279,6 +291,18 @@ pub unsafe fn restore_snapshot( if cur_module_hash != journal_module_hash { continue; } + + rewind = staging_rewind.take(); + for thread_id in staging_kill_thread.drain() { + spawn_threads.remove(&thread_id); + } + for thread in staging_spawn_threads.drain() { + spawn_threads.insert(thread.0, thread.1); + } + for mem in staging_update_memory.drain() { + update_memory.insert(mem.0, mem.1); + } + ctx.data_mut().pop_snapshot_trigger(trigger); } crate::journal::JournalEntry::SetClockTimeV1 { clock_id, time } => { @@ -597,6 +621,20 @@ pub unsafe fn restore_snapshot( } } + // Check for events that are orphaned + if !staging_kill_thread.is_empty() { + tracing::debug!("Orphaned thread exit (missing snapshot) - it will be ignored"); + } + if !staging_update_memory.is_empty() { + tracing::debug!("Orphaned memory update (missing snapshot) - it will be ignored"); + } + if !staging_spawn_threads.is_empty() { + tracing::debug!("Orphaned thread spawn (missing snapshot) - it will be ignored"); + } + if staging_rewind.is_some() { + tracing::debug!("Orphaned main rewind (missing snapshot) - it will be ignored"); + } + // If we are not in the same module then we fire off an exit // that simulates closing the process (hence keeps everything // in a clean state) @@ -608,8 +646,11 @@ pub unsafe fn restore_snapshot( ); if bootstrapping { rewind = None; + staging_rewind = None; spawn_threads.clear(); + staging_spawn_threads.clear(); update_memory.clear(); + staging_update_memory.clear(); update_tty.take(); stdout.clear(); stderr.clear(); From 6ea2ab22c75a058040f6d02f6a1f3df099db6f28 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Wed, 17 Jan 2024 17:56:06 +1100 Subject: [PATCH 10/67] Added the capability for the file system to index the data from a mmap backing store --- lib/virtual-fs/src/mem_fs/file.rs | 43 ++++ lib/virtual-fs/src/mem_fs/file_opener.rs | 75 ++++-- lib/virtual-fs/src/mem_fs/filesystem.rs | 20 ++ lib/virtual-fs/src/mem_fs/mod.rs | 17 ++ lib/virtual-fs/src/mem_fs/offloaded_file.rs | 255 ++++++++++++++++++++ 5 files changed, 389 insertions(+), 21 deletions(-) create mode 100644 lib/virtual-fs/src/mem_fs/offloaded_file.rs diff --git a/lib/virtual-fs/src/mem_fs/file.rs b/lib/virtual-fs/src/mem_fs/file.rs index fe3c5779875..9ac957d4f1e 100644 --- a/lib/virtual-fs/src/mem_fs/file.rs +++ b/lib/virtual-fs/src/mem_fs/file.rs @@ -151,6 +151,7 @@ impl VirtualFile for FileHandle { let inode = fs.storage.get(self.inode); match inode { Some(Node::File(node)) => node.file.len().try_into().unwrap_or(0), + Some(Node::OffloadedFile(node)) => node.file.len().try_into().unwrap_or(0), Some(Node::ReadOnlyFile(node)) => node.file.len().try_into().unwrap_or(0), Some(Node::CustomFile(node)) => { let file = node.file.lock().unwrap(); @@ -182,6 +183,10 @@ impl VirtualFile for FileHandle { .resize(new_size.try_into().map_err(|_| FsError::UnknownError)?, 0)?; metadata.len = new_size; } + Some(Node::OffloadedFile(OffloadedFileNode { file, metadata, .. })) => { + file.resize(new_size.try_into().map_err(|_| FsError::UnknownError)?, 0); + metadata.len = new_size; + } Some(Node::CustomFile(node)) => { let mut file = node.file.lock().unwrap(); file.set_len(new_size)?; @@ -337,6 +342,10 @@ impl VirtualFile for FileHandle { let remaining = node.file.buffer.len() - (self.cursor as usize); Poll::Ready(Ok(remaining)) } + Some(Node::OffloadedFile(node)) => { + let remaining = node.file.len() as usize - (self.cursor as usize); + Poll::Ready(Ok(remaining)) + } Some(Node::ReadOnlyFile(node)) => { let remaining = node.file.buffer.len() - (self.cursor as usize); Poll::Ready(Ok(remaining)) @@ -385,6 +394,7 @@ impl VirtualFile for FileHandle { let inode = fs.storage.get_mut(self.inode); match inode { Some(Node::File(_)) => Poll::Ready(Ok(8192)), + Some(Node::OffloadedFile(_)) => Poll::Ready(Ok(8192)), Some(Node::ReadOnlyFile(_)) => Poll::Ready(Ok(0)), Some(Node::CustomFile(node)) => { let mut file = node.file.lock().unwrap(); @@ -624,6 +634,17 @@ impl AsyncRead for FileHandle { } Poll::Ready(read.map(|_| ())) } + Some(Node::OffloadedFile(node)) => { + let read = unsafe { + node.file + .read(std::mem::transmute(buf.unfilled_mut()), &mut cursor) + }; + if let Ok(read) = &read { + unsafe { buf.assume_init(*read) }; + buf.advance(*read); + } + Poll::Ready(read.map(|_| ())) + } Some(Node::ReadOnlyFile(node)) => { let read = unsafe { node.file @@ -686,6 +707,10 @@ impl AsyncSeek for FileHandle { node.file.seek(position, &mut cursor)?; Ok(()) } + Some(Node::OffloadedFile(node)) => { + node.file.seek(position, &mut cursor)?; + Ok(()) + } Some(Node::ReadOnlyFile(node)) => { node.file.seek(position, &mut cursor)?; Ok(()) @@ -749,6 +774,7 @@ impl AsyncSeek for FileHandle { let inode = fs.storage.get_mut(self.inode); match inode { Some(Node::File { .. }) => Poll::Ready(Ok(self.cursor)), + Some(Node::OffloadedFile { .. }) => Poll::Ready(Ok(self.cursor)), Some(Node::ReadOnlyFile { .. }) => Poll::Ready(Ok(self.cursor)), Some(Node::CustomFile(node)) => { let mut file = node.file.lock().unwrap(); @@ -805,6 +831,11 @@ impl AsyncWrite for FileHandle { node.metadata.len = node.file.len().try_into().unwrap(); bytes_written } + Some(Node::OffloadedFile(node)) => { + let bytes_written = node.file.write(buf, &mut cursor)?; + node.metadata.len = node.file.len().try_into().unwrap(); + bytes_written + } Some(Node::ReadOnlyFile(node)) => { let bytes_written = node.file.write(buf, &mut cursor)?; node.metadata.len = node.file.len().try_into().unwrap(); @@ -880,6 +911,15 @@ impl AsyncWrite for FileHandle { node.metadata.len = node.file.buffer.len() as u64; Poll::Ready(Ok(bytes_written)) } + Some(Node::OffloadedFile(node)) => { + let buf = bufs + .iter() + .find(|b| !b.is_empty()) + .map_or(&[][..], |b| &**b); + let bytes_written = node.file.write(buf, &mut cursor)?; + node.metadata.len = node.file.len(); + Poll::Ready(Ok(bytes_written)) + } Some(Node::ReadOnlyFile(node)) => { let buf = bufs .iter() @@ -926,6 +966,7 @@ impl AsyncWrite for FileHandle { let inode = fs.storage.get_mut(self.inode); match inode { Some(Node::File(node)) => Poll::Ready(node.file.flush()), + Some(Node::OffloadedFile(node)) => Poll::Ready(node.file.flush()), Some(Node::ReadOnlyFile(node)) => Poll::Ready(node.file.flush()), Some(Node::CustomFile(node)) => { let mut file = node.file.lock().unwrap(); @@ -961,6 +1002,7 @@ impl AsyncWrite for FileHandle { let inode = fs.storage.get_mut(self.inode); match inode { Some(Node::File { .. }) => Poll::Ready(Ok(())), + Some(Node::OffloadedFile { .. }) => Poll::Ready(Ok(())), Some(Node::ReadOnlyFile { .. }) => Poll::Ready(Ok(())), Some(Node::CustomFile(node)) => { let mut file = node.file.lock().unwrap(); @@ -996,6 +1038,7 @@ impl AsyncWrite for FileHandle { let inode = fs.storage.get_mut(self.inode); match inode { Some(Node::File { .. }) => false, + Some(Node::OffloadedFile { .. }) => false, Some(Node::ReadOnlyFile { .. }) => false, Some(Node::CustomFile(node)) => { let file = node.file.lock().unwrap(); diff --git a/lib/virtual-fs/src/mem_fs/file_opener.rs b/lib/virtual-fs/src/mem_fs/file_opener.rs index 6a070d24486..2e69aa42678 100644 --- a/lib/virtual-fs/src/mem_fs/file_opener.rs +++ b/lib/virtual-fs/src/mem_fs/file_opener.rs @@ -402,6 +402,22 @@ impl crate::FileOpener for FileSystem { } } + Some(Node::OffloadedFile(OffloadedFileNode { metadata, file, .. })) => { + // Update the accessed time. + metadata.accessed = time(); + + // Truncate if needed. + if truncate { + file.truncate(); + metadata.len = 0; + } + + // Move the cursor to the end if needed. + if append { + cursor = file.len() as u64; + } + } + Some(Node::ReadOnlyFile(node)) => { // Update the accessed time. node.metadata.accessed = time(); @@ -470,29 +486,46 @@ impl crate::FileOpener for FileSystem { // Write lock. let mut fs = self.inner.write().map_err(|_| FsError::Lock)?; - let file = File::new(fs.limiter.clone()); - - // Creating the file in the storage. + let metadata = { + let time = time(); + Metadata { + ft: FileType { + file: true, + ..Default::default() + }, + accessed: time, + created: time, + modified: time, + len: 0, + } + }; let inode_of_file = fs.storage.vacant_entry().key(); - let real_inode_of_file = fs.storage.insert(Node::File(FileNode { - inode: inode_of_file, - name: name_of_file, - file, - metadata: { - let time = time(); - Metadata { - ft: FileType { - file: true, - ..Default::default() - }, - accessed: time, - created: time, - modified: time, - len: 0, - } - }, - })); + // We might be in optimized mode + let offload = { + let inner = self.inner.read().map_err(|_| FsError::Lock)?; + inner.mmap_offload.clone() + }; + let file = if let Some(offload) = offload { + let file = OffloadedFile::new(fs.limiter.clone(), offload); + Node::OffloadedFile(OffloadedFileNode { + inode: inode_of_file, + name: name_of_file, + file, + metadata, + }) + } else { + let file = File::new(fs.limiter.clone()); + Node::File(FileNode { + inode: inode_of_file, + name: name_of_file, + file, + metadata, + }) + }; + + // Creating the file in the storage. + let real_inode_of_file = fs.storage.insert(file); assert_eq!( inode_of_file, real_inode_of_file, diff --git a/lib/virtual-fs/src/mem_fs/filesystem.rs b/lib/virtual-fs/src/mem_fs/filesystem.rs index 1e344b745e9..4bbf26e07c0 100644 --- a/lib/virtual-fs/src/mem_fs/filesystem.rs +++ b/lib/virtual-fs/src/mem_fs/filesystem.rs @@ -3,6 +3,7 @@ use super::*; use crate::{DirEntry, FileSystem as _, FileType, FsError, Metadata, OpenOptions, ReadDir, Result}; use futures::future::BoxFuture; +use shared_buffer::OwnedBuffer; use slab::Slab; use std::collections::VecDeque; use std::convert::identity; @@ -29,6 +30,20 @@ impl FileSystem { self } + /// Uses a mmap'ed file as a cache for file data thus removing the + /// need to copy the data into memory. + /// + /// This is especially important for journals as it means that the + /// data stored within the journals does not need to be copied + /// into memory, for very large journals this would otherwise be + /// a problem. + pub fn with_mmap_offload(self, buffer: OwnedBuffer) -> Result { + let mut lock = self.inner.write().map_err(|_| FsError::Lock)?; + lock.mmap_offload.replace(buffer); + drop(lock); + Ok(self) + } + /// Canonicalize a path without validating that it actually exists. pub fn canonicalize_unchecked(&self, path: &Path) -> Result { let lock = self.inner.read().map_err(|_| FsError::Lock)?; @@ -619,6 +634,7 @@ impl fmt::Debug for FileSystem { /// indexed by their respective `Inode` in a slab. pub(super) struct FileSystemInner { pub(super) storage: Slab, + pub(super) mmap_offload: Option, pub(super) limiter: Option, } @@ -754,6 +770,7 @@ impl FileSystemInner { .filter_map(|(nth, inode)| self.storage.get(*inode).map(|node| (nth, node))) .find_map(|(nth, node)| match node { Node::File(FileNode { inode, name, .. }) + | Node::OffloadedFile(OffloadedFileNode { inode, name, .. }) | Node::ReadOnlyFile(ReadOnlyFileNode { inode, name, .. }) | Node::CustomFile(CustomFileNode { inode, name, .. }) | Node::ArcFile(ArcFileNode { inode, name, .. }) @@ -793,6 +810,7 @@ impl FileSystemInner { .filter_map(|(nth, inode)| self.storage.get(*inode).map(|node| (nth, node))) .find_map(|(nth, node)| match node { Node::File(FileNode { inode, name, .. }) + | Node::OffloadedFile(OffloadedFileNode { inode, name, .. }) | Node::Directory(DirectoryNode { inode, name, .. }) | Node::ReadOnlyFile(ReadOnlyFileNode { inode, name, .. }) | Node::CustomFile(CustomFileNode { inode, name, .. }) @@ -956,6 +974,7 @@ impl fmt::Debug for FileSystemInner { inode = node.inode(), ty = match node { Node::File { .. } => "file", + Node::OffloadedFile { .. } => "offloaded-file", Node::ReadOnlyFile { .. } => "ro-file", Node::ArcFile { .. } => "arc-file", Node::CustomFile { .. } => "custom-file", @@ -1015,6 +1034,7 @@ impl Default for FileSystemInner { Self { storage: slab, + mmap_offload: None, limiter: None, } } diff --git a/lib/virtual-fs/src/mem_fs/mod.rs b/lib/virtual-fs/src/mem_fs/mod.rs index 8d16dd784eb..26d16e2b2c8 100644 --- a/lib/virtual-fs/src/mem_fs/mod.rs +++ b/lib/virtual-fs/src/mem_fs/mod.rs @@ -1,6 +1,7 @@ mod file; mod file_opener; mod filesystem; +mod offloaded_file; mod stdio; use file::{File, FileHandle, ReadOnlyFile}; @@ -14,6 +15,8 @@ use std::{ sync::{Arc, Mutex}, }; +use self::offloaded_file::OffloadedFile; + type Inode = usize; const ROOT_INODE: Inode = 0; @@ -33,6 +36,14 @@ struct ReadOnlyFileNode { metadata: Metadata, } +#[derive(Debug)] +struct OffloadedFileNode { + inode: Inode, + name: OsString, + file: OffloadedFile, + metadata: Metadata, +} + #[derive(Debug)] struct ArcFileNode { inode: Inode, @@ -70,6 +81,7 @@ struct ArcDirectoryNode { #[derive(Debug)] enum Node { File(FileNode), + OffloadedFile(OffloadedFileNode), ReadOnlyFile(ReadOnlyFileNode), ArcFile(ArcFileNode), CustomFile(CustomFileNode), @@ -81,6 +93,7 @@ impl Node { fn inode(&self) -> Inode { *match self { Self::File(FileNode { inode, .. }) => inode, + Self::OffloadedFile(OffloadedFileNode { inode, .. }) => inode, Self::ReadOnlyFile(ReadOnlyFileNode { inode, .. }) => inode, Self::ArcFile(ArcFileNode { inode, .. }) => inode, Self::CustomFile(CustomFileNode { inode, .. }) => inode, @@ -92,6 +105,7 @@ impl Node { fn name(&self) -> &OsStr { match self { Self::File(FileNode { name, .. }) => name.as_os_str(), + Self::OffloadedFile(OffloadedFileNode { name, .. }) => name.as_os_str(), Self::ReadOnlyFile(ReadOnlyFileNode { name, .. }) => name.as_os_str(), Self::ArcFile(ArcFileNode { name, .. }) => name.as_os_str(), Self::CustomFile(CustomFileNode { name, .. }) => name.as_os_str(), @@ -103,6 +117,7 @@ impl Node { fn metadata(&self) -> &Metadata { match self { Self::File(FileNode { metadata, .. }) => metadata, + Self::OffloadedFile(OffloadedFileNode { metadata, .. }) => metadata, Self::ReadOnlyFile(ReadOnlyFileNode { metadata, .. }) => metadata, Self::ArcFile(ArcFileNode { metadata, .. }) => metadata, Self::CustomFile(CustomFileNode { metadata, .. }) => metadata, @@ -114,6 +129,7 @@ impl Node { fn metadata_mut(&mut self) -> &mut Metadata { match self { Self::File(FileNode { metadata, .. }) => metadata, + Self::OffloadedFile(OffloadedFileNode { metadata, .. }) => metadata, Self::ReadOnlyFile(ReadOnlyFileNode { metadata, .. }) => metadata, Self::ArcFile(ArcFileNode { metadata, .. }) => metadata, Self::CustomFile(CustomFileNode { metadata, .. }) => metadata, @@ -125,6 +141,7 @@ impl Node { fn set_name(&mut self, new_name: OsString) { match self { Self::File(FileNode { name, .. }) => *name = new_name, + Self::OffloadedFile(OffloadedFileNode { name, .. }) => *name = new_name, Self::ReadOnlyFile(ReadOnlyFileNode { name, .. }) => *name = new_name, Self::ArcFile(ArcFileNode { name, .. }) => *name = new_name, Self::CustomFile(CustomFileNode { name, .. }) => *name = new_name, diff --git a/lib/virtual-fs/src/mem_fs/offloaded_file.rs b/lib/virtual-fs/src/mem_fs/offloaded_file.rs new file mode 100644 index 00000000000..ed842772336 --- /dev/null +++ b/lib/virtual-fs/src/mem_fs/offloaded_file.rs @@ -0,0 +1,255 @@ +use bytes::Bytes; +use shared_buffer::OwnedBuffer; +use std::{cmp, io}; + +use crate::limiter::DynFsMemoryLimiter; + +#[derive(Debug)] +pub enum FileExtent { + MmapOffload { offset: u64, size: u64 }, + RepeatingBytes { value: u8, cnt: u64 }, + Bytes { data: Bytes }, +} + +impl FileExtent { + pub fn size(&self) -> u64 { + match self { + FileExtent::MmapOffload { size, .. } => *size, + FileExtent::RepeatingBytes { cnt, .. } => *cnt, + FileExtent::Bytes { data } => data.len() as u64, + } + } + + pub fn resize(&mut self, new_size: u64) { + match self { + FileExtent::MmapOffload { size, .. } => *size = new_size.min(*size), + FileExtent::RepeatingBytes { cnt, .. } => *cnt = new_size, + FileExtent::Bytes { data } => { + *data = data.slice(..(new_size as usize)); + } + } + } +} + +#[derive(Debug)] +pub struct OffloadedFile { + mmap_offload: OwnedBuffer, + #[allow(dead_code)] + limiter: Option, + extents: Vec, +} + +impl OffloadedFile { + pub fn new(limiter: Option, buffer: OwnedBuffer) -> Self { + Self { + mmap_offload: buffer, + limiter, + extents: Vec::new(), + } + } + + pub fn seek(&self, position: io::SeekFrom, cursor: &mut u64) -> io::Result { + let to_err = |_| io::ErrorKind::InvalidInput; + + // Calculate the next cursor. + let next_cursor: i64 = match position { + io::SeekFrom::Start(offset) => offset.try_into().map_err(to_err)?, + io::SeekFrom::End(offset) => self.len() as i64 + offset, + io::SeekFrom::Current(offset) => { + TryInto::::try_into(*cursor).map_err(to_err)? + offset + } + }; + + // It's an error to seek before byte 0. + if next_cursor < 0 { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "seeking before the byte 0", + )); + } + + // In this implementation, it's an error to seek beyond the + // end of the buffer. + let next_cursor = next_cursor.try_into().map_err(to_err)?; + *cursor = cmp::min(self.len() as u64, next_cursor); + Ok(*cursor) + } + + pub fn read(&self, mut buf: &mut [u8], cursor: &mut u64) -> io::Result { + let cursor_start = *cursor; + + let mut extent_offset = cursor_start; + let mut extent_index = 0usize; + while buf.len() > 0 && extent_index < self.extents.len() { + let extent = &self.extents[extent_index]; + + if extent_offset >= extent.size() { + extent_offset -= extent.size(); + extent_index += 1; + continue; + } + + let read = match extent { + FileExtent::MmapOffload { + offset: mmap_offset, + size: extent_size, + } => { + let mmap_offset = mmap_offset + extent_offset; + let data = &self.mmap_offload.as_slice()[mmap_offset as usize..]; + let data = &data[..(*extent_size - extent_offset) as usize]; + let data_len = cmp::min(buf.len(), data.len()); + buf[..data_len].copy_from_slice(&data[..data_len]); + data_len + } + FileExtent::RepeatingBytes { value, cnt } => { + let cnt = cmp::min(buf.len() as u64, cnt - extent_offset) as usize; + buf[..cnt].iter_mut().for_each(|d| { + *d = *value; + }); + cnt + } + FileExtent::Bytes { data } => { + let data = &data.as_ref()[extent_offset as usize..]; + let data_len = cmp::min(buf.len(), data.len()); + buf[..data_len].copy_from_slice(&data[..data_len]); + data_len + } + }; + + *cursor += read as u64; + extent_offset = 0; + extent_index += 1; + buf = &mut buf[read..]; + } + Ok((*cursor - cursor_start) as usize) + } + + pub fn write(&mut self, data: &[u8], cursor: &mut u64) -> io::Result { + let mut extent_offset = *cursor; + let mut data_len = data.len() as u64; + + // We need to split any extents that are intersecting with the + // start or end of the new block of data we are about to write + let mut split_extents = |mut split_at: u64| { + let mut index = 0usize; + while split_at > 0 && index < self.extents.len() { + let extent = &mut self.extents[index]; + if split_at >= extent.size() { + split_at -= extent.size(); + index += 1; + continue; + } else if split_at == 0 { + break; + } else { + let new_extent = match extent { + FileExtent::MmapOffload { + offset: other_offset, + size: other_size, + } => FileExtent::MmapOffload { + offset: *other_offset + split_at, + size: *other_size - split_at, + }, + FileExtent::RepeatingBytes { + value: other_value, + cnt: other_cnt, + } => FileExtent::RepeatingBytes { + value: *other_value, + cnt: *other_cnt - split_at, + }, + FileExtent::Bytes { data: other_data } => FileExtent::Bytes { + data: other_data.slice((split_at as usize)..), + }, + }; + extent.resize(split_at); + self.extents.insert(index + 1, new_extent); + break; + } + } + }; + split_extents(extent_offset); + split_extents(extent_offset + data_len); + + // Now we delete all the extents that exist between the + // range that we are about to insert + let mut index = 0usize; + while extent_offset > 0 && index < self.extents.len() { + let extent = &self.extents[index]; + if extent_offset > extent.size() { + extent_offset -= extent.size(); + index += 1; + continue; + } else { + while index < self.extents.len() { + let extent = &self.extents[index]; + if data_len < extent.size() { + break; + } + data_len -= extent.size(); + self.extents.remove(index); + } + break; + } + } + + // Finally we add the new extent + let data_start = data.as_ptr() as u64; + let data_end = data_start + data.len() as u64; + let mmap_start = self.mmap_offload.as_slice().as_ptr() as u64; + let mmap_end = mmap_start + self.mmap_offload.as_slice().len() as u64; + + // If the data is within the mmap buffer then we use a extent range + // to represent the data, otherwise we fall back on copying the data + let new_extent = if data_start >= mmap_start && data_end <= mmap_end { + FileExtent::MmapOffload { + offset: data_start - mmap_start, + size: data_end - data_start, + } + } else { + FileExtent::Bytes { + data: data.to_vec().into(), + } + }; + self.extents.insert(index, new_extent); + + // Update the cursor + *cursor += data.len() as u64; + Ok(data.len()) + } + + pub fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + pub fn resize(&mut self, new_len: u64, value: u8) { + let mut cur_len = self.len(); + if new_len > cur_len { + self.extents.push(FileExtent::RepeatingBytes { + value, + cnt: new_len - cur_len, + }); + } + while cur_len > new_len && !self.extents.is_empty() { + let extent: &mut FileExtent = self.extents.last_mut().unwrap(); + let diff = extent.size().min(cur_len - new_len); + extent.resize(extent.size() - diff); + cur_len -= diff; + if extent.size() <= 0 { + self.extents.pop(); + } + } + } + + pub fn len(&self) -> u64 { + self.extents.iter().map(FileExtent::size).sum() + } + + pub fn truncate(&mut self) { + self.extents.clear(); + } +} + +#[cfg(test)] +mod tests { + #[test] + pub fn test_resize() {} +} From a40d69b99b89fc4e9c99ada259c64551547a16e8 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 22 Jan 2024 09:57:41 +1100 Subject: [PATCH 11/67] Added unit tests for offload file --- Cargo.lock | 1 + lib/virtual-fs/Cargo.toml | 1 + lib/virtual-fs/src/mem_fs/offloaded_file.rs | 105 ++++++++++++++++++-- 3 files changed, 96 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be03d67e0cd..b173e99a6a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5336,6 +5336,7 @@ dependencies = [ "thiserror", "tokio", "tracing", + "tracing-test", "typetag", "webc", ] diff --git a/lib/virtual-fs/Cargo.toml b/lib/virtual-fs/Cargo.toml index 6b86ab92519..a4d8e776b33 100644 --- a/lib/virtual-fs/Cargo.toml +++ b/lib/virtual-fs/Cargo.toml @@ -40,6 +40,7 @@ getrandom = { version = "0.2", features = [ "js" ] } [dev-dependencies] pretty_assertions = "1.3.0" tempfile = "3.6.0" +tracing-test = "0.2.4" tokio = { version = "1", features = ["io-util", "rt"], default_features = false } [features] diff --git a/lib/virtual-fs/src/mem_fs/offloaded_file.rs b/lib/virtual-fs/src/mem_fs/offloaded_file.rs index ed842772336..20cf8a69887 100644 --- a/lib/virtual-fs/src/mem_fs/offloaded_file.rs +++ b/lib/virtual-fs/src/mem_fs/offloaded_file.rs @@ -172,24 +172,24 @@ impl OffloadedFile { // Now we delete all the extents that exist between the // range that we are about to insert let mut index = 0usize; - while extent_offset > 0 && index < self.extents.len() { + while index < self.extents.len() { let extent = &self.extents[index]; - if extent_offset > extent.size() { + if extent_offset >= extent.size() { extent_offset -= extent.size(); index += 1; continue; } else { - while index < self.extents.len() { - let extent = &self.extents[index]; - if data_len < extent.size() { - break; - } - data_len -= extent.size(); - self.extents.remove(index); - } break; } } + while index < self.extents.len() { + let extent = &self.extents[index]; + if data_len < extent.size() { + break; + } + data_len -= extent.size(); + self.extents.remove(index); + } // Finally we add the new extent let data_start = data.as_ptr() as u64; @@ -250,6 +250,89 @@ impl OffloadedFile { #[cfg(test)] mod tests { + use super::*; + #[test] - pub fn test_resize() {} + #[tracing_test::traced_test] + pub fn test_offload_file() -> anyhow::Result<()> { + let buffer = OwnedBuffer::from_bytes(std::iter::repeat(12u8).take(100).collect::>()); + let test_data2 = buffer.clone(); + + let mut file = OffloadedFile::new(None, buffer); + + let mut cursor = 0u64; + let test_data = std::iter::repeat(56u8).take(100).collect::>(); + file.write(&test_data, &mut cursor)?; + + assert_eq!(file.len(), 100); + + cursor = 0; + let mut result = std::iter::repeat(0u8).take(100).collect::>(); + file.read(&mut result, &mut cursor)?; + assert_eq!( + &result, + &std::iter::repeat(56u8).take(100).collect::>() + ); + + cursor = 50; + file.write(&test_data2, &mut cursor)?; + + assert_eq!(file.len(), 150); + + cursor = 0; + let mut result = std::iter::repeat(0u8).take(150).collect::>(); + file.read(&mut result, &mut cursor)?; + assert_eq!( + &result, + &std::iter::repeat(56u8) + .take(50) + .chain(std::iter::repeat(12u8).take(100)) + .collect::>() + ); + + file.resize(200, 99u8); + assert_eq!(file.len(), 200); + + cursor = 0; + let mut result = std::iter::repeat(0u8).take(200).collect::>(); + file.read(&mut result, &mut cursor)?; + assert_eq!( + &result, + &std::iter::repeat(56u8) + .take(50) + .chain(std::iter::repeat(12u8).take(100)) + .chain(std::iter::repeat(99u8).take(50)) + .collect::>() + ); + + file.resize(33, 01u8); + + cursor = 0; + let mut result = std::iter::repeat(0u8).take(33).collect::>(); + file.read(&mut result, &mut cursor)?; + assert_eq!( + &result, + &std::iter::repeat(56u8).take(33).collect::>() + ); + + let mut cursor = 10u64; + let test_data = std::iter::repeat(74u8).take(10).collect::>(); + file.write(&test_data, &mut cursor)?; + + assert_eq!(file.len(), 33); + + cursor = 0; + let mut result = std::iter::repeat(0u8).take(33).collect::>(); + file.read(&mut result, &mut cursor)?; + assert_eq!( + &result, + &std::iter::repeat(56u8) + .take(10) + .chain(std::iter::repeat(74u8).take(10)) + .chain(std::iter::repeat(56u8).take(13)) + .collect::>() + ); + + Ok(()) + } } From 026636dde26344f57265aaf54c24d07e235f25ec Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Tue, 23 Jan 2024 03:07:19 +1100 Subject: [PATCH 12/67] Fixed some alignment and serialization issues on the journals --- Cargo.lock | 125 ++- lib/cli/Cargo.toml | 7 +- lib/cli/src/commands/journal/import.rs | 6 +- lib/cli/src/commands/journal/inspect.rs | 6 +- lib/cli/src/commands/journal/mod.rs | 13 +- lib/cli/src/commands/journal/mount/cmd.rs | 42 + lib/cli/src/commands/journal/mount/fs.rs | 615 +++++++++++++ lib/cli/src/commands/journal/mount/mod.rs | 4 + lib/journal/src/base64.rs | 3 +- lib/journal/src/concrete/archived.rs | 962 ++++++++++---------- lib/journal/src/concrete/archived_from.rs | 63 +- lib/journal/src/concrete/log_file.rs | 72 +- lib/journal/src/concrete/printing.rs | 8 +- lib/virtual-fs/src/mem_fs/offloaded_file.rs | 12 +- lib/wasix/src/fs/mod.rs | 85 +- lib/wasix/src/journal/concrete/archived.rs | 90 +- lib/wasix/src/state/env.rs | 5 +- 17 files changed, 1440 insertions(+), 678 deletions(-) create mode 100644 lib/cli/src/commands/journal/mount/cmd.rs create mode 100644 lib/cli/src/commands/journal/mount/fs.rs create mode 100644 lib/cli/src/commands/journal/mount/mod.rs diff --git a/Cargo.lock b/Cargo.lock index b173e99a6a2..8a33c75a8c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -239,7 +239,7 @@ checksum = "a1e9efbe14612da0a19fb983059a0b621e9cf6225d7018ecab4f9988215540dc" dependencies = [ "futures-io", "futures-util", - "log", + "log 0.4.20", "pin-project-lite", "tungstenite", ] @@ -479,7 +479,7 @@ checksum = "4b922faaf31122819ec80c4047cc684c6979a087366c069611e33649bf98e18d" dependencies = [ "heck 0.4.1", "indexmap 1.9.3", - "log", + "log 0.4.20", "proc-macro2", "quote", "serde", @@ -573,7 +573,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c90e95e5bd4e8ac34fa6f37c774b0c6f8ed06ea90c79931fd448fcf941a9767" dependencies = [ "clap", - "log", + "log 0.4.20", ] [[package]] @@ -680,7 +680,7 @@ dependencies = [ "getopts", "lazy_static", "libc", - "log", + "log 0.4.20", "miow", "regex", "rustfix", @@ -798,7 +798,7 @@ dependencies = [ "cranelift-isle", "gimli 0.26.2", "hashbrown 0.12.3", - "log", + "log 0.4.20", "regalloc2", "smallvec", "target-lexicon 0.12.12", @@ -829,7 +829,7 @@ dependencies = [ "fxhash", "hashbrown 0.12.3", "indexmap 1.9.3", - "log", + "log 0.4.20", "smallvec", ] @@ -847,7 +847,7 @@ checksum = "0d70abacb8cfef3dc8ff7e8836e9c1d70f7967dfdac824a4cd5e30223415aca6" dependencies = [ "cranelift-codegen", "hashbrown 0.12.3", - "log", + "log 0.4.20", "smallvec", "target-lexicon 0.12.12", ] @@ -1400,7 +1400,7 @@ dependencies = [ "serde_path_to_error", "serde_yaml 0.8.26", "sparx", - "time", + "time 0.3.31", "url", "uuid", "wcgi-host", @@ -1424,7 +1424,7 @@ dependencies = [ "serde_path_to_error", "serde_yaml 0.8.26", "sparx", - "time", + "time 0.3.31", "url", "uuid", "wcgi-host", @@ -1585,7 +1585,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" dependencies = [ "colored 1.9.4", - "log", + "log 0.4.20", ] [[package]] @@ -1677,6 +1677,19 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "fuse" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e57070510966bfef93662a81cb8aa2b1c7db0964354fa9921434f04b9e8660" +dependencies = [ + "libc", + "log 0.3.9", + "pkg-config", + "thread-scoped", + "time 0.1.45", +] + [[package]] name = "futures" version = "0.3.29" @@ -1803,7 +1816,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -1869,7 +1882,7 @@ dependencies = [ "async-tungstenite", "futures", "graphql_client 0.13.0", - "log", + "log 0.4.20", "pin-project", "serde", "serde_json", @@ -2607,6 +2620,15 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "log" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" +dependencies = [ + "log 0.4.20", +] + [[package]] name = "log" version = "0.4.20" @@ -2795,8 +2817,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", - "log", - "wasi", + "log 0.4.20", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.48.0", ] @@ -2823,7 +2845,7 @@ checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", - "log", + "log 0.4.20", "openssl", "openssl-probe", "openssl-sys", @@ -3571,7 +3593,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" dependencies = [ "fxhash", - "log", + "log 0.4.20", "slice-group-by", "smallvec", ] @@ -3667,7 +3689,7 @@ dependencies = [ "hyper-tls", "ipnet", "js-sys", - "log", + "log 0.4.20", "mime", "mime_guess", "native-tls", @@ -3826,7 +3848,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2c50b74badcddeb8f7652fa8323ce440b95286f8e4b64ebfd871c609672704e" dependencies = [ "anyhow", - "log", + "log 0.4.20", "serde", "serde_json", ] @@ -3850,7 +3872,7 @@ version = "0.21.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ - "log", + "log 0.4.20", "ring", "rustls-webpki", "sct", @@ -4261,7 +4283,7 @@ dependencies = [ "dashmap", "futures", "lazy_static", - "log", + "log 0.4.20", "parking_lot 0.12.1", "serial_test_derive 2.0.0", ] @@ -4686,6 +4708,12 @@ dependencies = [ "syn 2.0.42", ] +[[package]] +name = "thread-scoped" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcbb6aa301e5d3b0b5ef639c9a9c7e2f1c944f177b460c04dc24c69b1fa2bd99" + [[package]] name = "thread_local" version = "1.1.7" @@ -4696,6 +4724,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + [[package]] name = "time" version = "0.3.31" @@ -4757,7 +4796,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec03259a0567ad58eed30812bc3e5eda8030f154abc70317ab57b14f00699ca4" dependencies = [ "idna 0.2.3", - "log", + "log 0.4.20", "regex", "serde_json", "thiserror", @@ -4837,7 +4876,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", - "log", + "log 0.4.20", "rustls", "rustls-native-certs", "tokio", @@ -4854,7 +4893,7 @@ dependencies = [ "bytes", "futures-core", "futures-sink", - "log", + "log 0.4.20", "pin-project-lite", "tokio", ] @@ -4995,7 +5034,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "log", + "log 0.4.20", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -5028,7 +5067,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ - "log", + "log 0.4.20", "once_cell", "tracing-core", ] @@ -5117,7 +5156,7 @@ dependencies = [ "data-encoding", "http", "httparse", - "log", + "log 0.4.20", "rand", "rustls", "sha1", @@ -5578,6 +5617,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -5622,7 +5667,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", - "log", + "log 0.4.20", "once_cell", "proc-macro2", "quote", @@ -5828,7 +5873,7 @@ dependencies = [ "serde", "serde_json", "serde_path_to_error", - "time", + "time 0.3.31", "tokio", "tracing", "url", @@ -5932,6 +5977,7 @@ dependencies = [ "dirs", "distance", "flate2", + "fuse", "futures", "hex", "http", @@ -5941,7 +5987,7 @@ dependencies = [ "indicatif", "is-terminal", "libc", - "log", + "log 0.4.20", "object 0.30.4", "once_cell", "opener", @@ -5960,6 +6006,7 @@ dependencies = [ "target-lexicon 0.12.12", "tempfile", "thiserror", + "time 0.1.45", "tldextract", "tokio", "toml 0.5.11", @@ -6031,7 +6078,7 @@ dependencies = [ "distance", "fern", "is-terminal", - "log", + "log 0.4.20", "target-lexicon 0.12.12", "unix_mode", "wasmer-compiler", @@ -6128,7 +6175,7 @@ dependencies = [ "edge-util", "futures", "is-terminal", - "log", + "log 0.4.20", "once_cell", "parking_lot 0.12.1", "regex", @@ -6137,7 +6184,7 @@ dependencies = [ "serde", "serde_json", "serde_yaml 0.8.26", - "time", + "time 0.3.31", "tokio", "toml 0.7.8", "tracing", @@ -6160,8 +6207,8 @@ dependencies = [ "getrandom", "lazy_static", "libc", - "log", - "time", + "log 0.4.20", + "time 0.3.31", "wasmer", "wasmer-types", ] @@ -6289,7 +6336,7 @@ dependencies = [ "indexmap 1.9.3", "indicatif", "lazy_static", - "log", + "log 0.4.20", "lzma-rs", "minisign", "regex", @@ -6302,7 +6349,7 @@ dependencies = [ "tar", "tempfile", "thiserror", - "time", + "time 0.3.31", "tldextract", "tokio", "toml 0.5.11", @@ -6333,7 +6380,7 @@ dependencies = [ "indexmap 1.9.3", "indicatif", "lazy_static", - "log", + "log 0.4.20", "lzma-rs", "minisign", "pretty_assertions", @@ -6347,7 +6394,7 @@ dependencies = [ "tar", "tempfile", "thiserror", - "time", + "time 0.3.31", "tldextract", "tokio", "tokio-tungstenite", @@ -6543,7 +6590,7 @@ dependencies = [ "num_enum", "pretty_assertions", "serde", - "time", + "time 0.3.31", "tracing", "wai-bindgen-gen-core", "wai-bindgen-gen-rust", diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index b5218b35fec..bee808e02a4 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -68,8 +68,11 @@ virtual-net = { version = "0.6.2", path = "../virtual-net" } webc = { workspace = true } wasmer-edge-cli = { version = "=0.1.4", default-features = false } -# Third-party dependencies. +# Used by the mount command +fuse = { version = "0.3", optional = true } +time = { version = "0.1.45", optional = true } +# Third-party dependencies. is-terminal = "0.4.7" colored = "2.0" anyhow = "1.0" @@ -166,10 +169,12 @@ default = [ "wast", "compiler", "journal", + "fuse", "wasmer-artifact-create", "static-artifact-create", ] journal = ["wasmer-wasix/journal"] +fuse = ["dep:fuse", "dep:time"] backend = [] coredump = ["wasm-coredump-builder"] sys = ["compiler", "wasmer-vm"] diff --git a/lib/cli/src/commands/journal/import.rs b/lib/cli/src/commands/journal/import.rs index e0377e05ee1..96d0e8f41cf 100644 --- a/lib/cli/src/commands/journal/import.rs +++ b/lib/cli/src/commands/journal/import.rs @@ -7,19 +7,19 @@ use wasmer_wasix::journal::{JournalEntry, LogFileJournal, WritableJournal}; /// Imports events into a journal file. Events are streamed as JSON /// objects into `stdin` #[derive(Debug, Parser)] -pub struct CmdJournaImport { +pub struct CmdJournalImport { /// Path to the journal that will be printed #[clap(index = 1)] journal_path: PathBuf, } -impl AsyncCliCommand for CmdJournaImport { +impl AsyncCliCommand for CmdJournalImport { fn run_async(self) -> futures::future::BoxFuture<'static, Result<(), anyhow::Error>> { Box::pin(self.run()) } } -impl CmdJournaImport { +impl CmdJournalImport { async fn run(self) -> Result<(), anyhow::Error> { // Erase the journal file at the path and reopen it if self.journal_path.exists() { diff --git a/lib/cli/src/commands/journal/inspect.rs b/lib/cli/src/commands/journal/inspect.rs index 9a19e56d77f..cbdc7ddc732 100644 --- a/lib/cli/src/commands/journal/inspect.rs +++ b/lib/cli/src/commands/journal/inspect.rs @@ -6,19 +6,19 @@ use wasmer_wasix::journal::{copy_journal, LogFileJournal, PrintingJournal}; /// Prints a summarized version of contents of a journal to stdout #[derive(Debug, Parser)] -pub struct CmdJournaInspect { +pub struct CmdJournalInspect { /// Path to the journal that will be printed #[clap(index = 1)] journal_path: PathBuf, } -impl AsyncCliCommand for CmdJournaInspect { +impl AsyncCliCommand for CmdJournalInspect { fn run_async(self) -> futures::future::BoxFuture<'static, Result<(), anyhow::Error>> { Box::pin(self.run()) } } -impl CmdJournaInspect { +impl CmdJournalInspect { async fn run(self) -> Result<(), anyhow::Error> { let journal = LogFileJournal::new(self.journal_path)?; let printer = PrintingJournal::default(); diff --git a/lib/cli/src/commands/journal/mod.rs b/lib/cli/src/commands/journal/mod.rs index 524cc0c6463..8604c0b5ebe 100644 --- a/lib/cli/src/commands/journal/mod.rs +++ b/lib/cli/src/commands/journal/mod.rs @@ -5,12 +5,16 @@ mod export; mod filter; mod import; mod inspect; +#[cfg(feature = "fuse")] +mod mount; pub use compact::*; pub use export::*; pub use filter::*; pub use import::*; pub use inspect::*; +#[cfg(feature = "fuse")] +pub use mount::*; /// Manage Journal files. #[derive(clap::Subcommand, Debug)] @@ -20,11 +24,14 @@ pub enum CmdJournal { /// Exports the contents of a journal to stdout as JSON objects Export(CmdJournalExport), /// Imports the events into a journal as JSON objects - Import(CmdJournaImport), + Import(CmdJournalImport), /// Inspects the contents of a journal and summarizes it to `stdout` - Inspect(CmdJournaInspect), + Inspect(CmdJournalInspect), /// Filters out certain events from a journal Filter(CmdJournalFilter), + /// Mounts the journal at a particular directory + #[cfg(feature = "fuse")] + Mount(CmdJournalMount), } impl AsyncCliCommand for CmdJournal { @@ -35,6 +42,8 @@ impl AsyncCliCommand for CmdJournal { Self::Export(cmd) => cmd.run_async(), Self::Inspect(cmd) => cmd.run_async(), Self::Filter(cmd) => cmd.run_async(), + #[cfg(feature = "fuse")] + Self::Mount(cmd) => cmd.run_async(), } } } diff --git a/lib/cli/src/commands/journal/mount/cmd.rs b/lib/cli/src/commands/journal/mount/cmd.rs new file mode 100644 index 00000000000..0a79bbfd419 --- /dev/null +++ b/lib/cli/src/commands/journal/mount/cmd.rs @@ -0,0 +1,42 @@ +use std::{path::PathBuf, process::Stdio}; + +use clap::Parser; +use wasmer_edge_cli::cmd::AsyncCliCommand; +use wasmer_wasix::fs::WasiFdSeed; + +use super::fs::JournalFileSystem; + +/// Mounts a journal as a file system on the local machine +#[derive(Debug, Parser)] +pub struct CmdJournalMount { + /// Path to the journal that will be printed + #[clap(index = 1)] + journal_path: PathBuf, + /// Path to the directory where the file system will be mounted + #[clap(index = 2)] + mount_path: PathBuf, +} + +impl AsyncCliCommand for CmdJournalMount { + fn run_async(self) -> futures::future::BoxFuture<'static, Result<(), anyhow::Error>> { + Box::pin(self.run()) + } +} + +impl CmdJournalMount { + async fn run(self) -> Result<(), anyhow::Error> { + // First we unmount any existing file system on this path + std::process::Command::new("/bin/umount") + .arg(self.mount_path.to_string_lossy().as_ref()) + .stderr(Stdio::null()) + .stdout(Stdio::null()) + .spawn()? + .wait() + .ok(); + + // Mounts the journal file system at a path + let fs = JournalFileSystem::new(&self.journal_path, WasiFdSeed::default())?; + fuse::mount(fs, &self.mount_path, &[])?; + Ok(()) + } +} diff --git a/lib/cli/src/commands/journal/mount/fs.rs b/lib/cli/src/commands/journal/mount/fs.rs new file mode 100644 index 00000000000..ab9c8e068da --- /dev/null +++ b/lib/cli/src/commands/journal/mount/fs.rs @@ -0,0 +1,615 @@ +#![allow(unused)] +use std::{ + borrow::Cow, + collections::HashMap, + ffi::OsStr, + hash::{Hash, Hasher}, + io, + path::Path, + sync::{atomic::AtomicU32, Arc, Mutex}, + time::Duration, +}; + +use fuse::{ + FileAttr, Filesystem, ReplyAttr, ReplyCreate, ReplyData, ReplyDirectory, ReplyEmpty, + ReplyEntry, ReplyOpen, Request, +}; +use tokio::runtime::Handle; +use virtual_fs::{mem_fs, AsyncReadExt, AsyncSeekExt, AsyncWriteExt, FileSystem, FsError}; +use wasmer_wasix::{ + fs::WasiFdSeed, + journal::{ + copy_journal, Journal, JournalEntry, LogFileJournal, ReadableJournal, WritableJournal, + }, + types::Oflags, + wasmer_wasix_types::wasi, + VIRTUAL_ROOT_FD, +}; + +#[derive(Debug)] +struct State { + handle: tokio::runtime::Handle, + mem_fs: mem_fs::FileSystem, + inos: HashMap>, + fuse_lookup: HashMap>, + seed: WasiFdSeed, + journal_lookup: HashMap< + u32, + Arc>>, + >, +} + +#[derive(Debug)] +struct MutexState { + inner: Mutex, +} + +#[derive(Debug)] +pub struct JournalFileSystem { + handle: tokio::runtime::Handle, + journal: LogFileJournal, + state: MutexState, +} + +impl JournalFileSystem { + // Opens the journal and copies all its contents into + // and memory file system + pub fn new(journal_path: &Path, fd_seed: WasiFdSeed) -> anyhow::Result { + let journal = LogFileJournal::new(journal_path)?; + + let mem_fs = mem_fs::FileSystem::default(); + let state = MutexState { + inner: Mutex::new(State { + handle: tokio::runtime::Handle::current(), + mem_fs, + inos: Default::default(), + seed: fd_seed, + fuse_lookup: Default::default(), + journal_lookup: Default::default(), + }), + }; + copy_journal(&journal, &state)?; + + let ret = Self { + handle: tokio::runtime::Handle::current(), + journal, + state, + }; + + Ok(ret) + } + + fn reverse_ino(&self, ino: u64) -> Result, libc::c_int> { + if ino == 1 { + return Ok("/".into()); + } + let path = { + let mut state = self.state.inner.lock().unwrap(); + match state.inos.get(&ino).cloned() { + Some(path) => path, + None => { + return Err(libc::ENOENT); + } + } + }; + Ok(path) + } + + fn attr<'a>(&self, name: Cow<'a, str>) -> Result { + let mut state = self.state.inner.lock().unwrap(); + + let res = state.mem_fs.metadata(&Path::new(name.as_ref())); + match res { + Ok(meta) => { + // The ino is just the hash of the name + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + name.hash(&mut hasher); + let ino = hasher.finish(); + state + .inos + .entry(ino) + .or_insert_with(|| name.into_owned().into()); + + // Build a file attr and return it + Ok(FileAttr { + ino, + size: meta.len, + blocks: (1u64.max(meta.len) - 1 / 512) + 1, + atime: time::Timespec::new(meta.accessed as i64, 0), + mtime: time::Timespec::new(meta.modified as i64, 0), + ctime: time::Timespec::new(meta.created as i64, 0), + crtime: time::Timespec::new(meta.created as i64, 0), + kind: file_type_to_kind(meta.ft), + perm: 0o644, + nlink: 1, + uid: 0, + gid: 0, + rdev: 0, + flags: 0, + }) + } + Err(FsError::EntryNotFound) => Err(libc::ENOENT), + Err(_) => Err(libc::EIO), + } + } +} + +impl WritableJournal for MutexState { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + let ret = entry.estimate_size() as u64; + let mut state = self.inner.lock().unwrap(); + match entry { + JournalEntry::FileDescriptorWriteV1 { + fd, + offset, + data, + is_64bit, + } => { + let handle = state.handle.clone(); + if let Some(file) = state.journal_lookup.get_mut(&fd) { + handle.block_on(async { + let mut file = file.lock().await; + file.seek(io::SeekFrom::Start(offset)).await; + file.write_all(&data).await + })?; + } + } + JournalEntry::CloseFileDescriptorV1 { fd } => { + state.journal_lookup.remove(&fd); + } + JournalEntry::OpenFileDescriptorV1 { + fd, + dirfd, + dirflags, + path, + o_flags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + } => { + state.seed.clip_val(fd + 1); + let file = state + .mem_fs + .new_open_options() + .create(o_flags.contains(Oflags::CREATE)) + .truncate(o_flags.contains(Oflags::TRUNC)) + .open(path.as_ref())?; + state + .journal_lookup + .insert(fd, Arc::new(tokio::sync::Mutex::new(file))); + } + JournalEntry::RenumberFileDescriptorV1 { old_fd, new_fd } => { + state.seed.clip_val(new_fd + 1); + if let Some(file) = state.journal_lookup.remove(&old_fd) { + state.journal_lookup.insert(new_fd, file); + } + } + JournalEntry::DuplicateFileDescriptorV1 { + original_fd, + copied_fd, + } => { + state.seed.clip_val(copied_fd + 1); + if let Some(file) = state.journal_lookup.get(&original_fd).cloned() { + state.journal_lookup.insert(copied_fd, file); + } + } + JournalEntry::CreateDirectoryV1 { fd, path } => { + state.mem_fs.create_dir(&Path::new(path.as_ref())).ok(); + } + JournalEntry::RemoveDirectoryV1 { fd, path } => { + state.mem_fs.remove_dir(&Path::new(path.as_ref()))?; + } + JournalEntry::FileDescriptorSetSizeV1 { fd, st_size } => { + let handle = state.handle.clone(); + if let Some(file) = state.journal_lookup.get(&fd) { + handle.block_on(async { + let mut file = file.lock().await; + file.set_len(st_size) + })?; + } + } + JournalEntry::FileDescriptorAllocateV1 { fd, offset, len } => { + let handle = state.handle.clone(); + if let Some(file) = state.journal_lookup.get(&fd) { + handle.block_on(async { + let mut file = file.lock().await; + file.set_len(offset + len) + })?; + } + } + JournalEntry::UnlinkFileV1 { fd, path } => { + state.mem_fs.remove_file(&Path::new(path.as_ref()))?; + } + JournalEntry::PathRenameV1 { + old_fd, + old_path, + new_fd, + new_path, + } => { + let handle = state.handle.clone(); + handle.block_on(async { + state + .mem_fs + .rename(&Path::new(old_path.as_ref()), &Path::new(new_path.as_ref())) + .await + })?; + } + _ => {} + } + Ok(ret) + } +} + +impl JournalFileSystem { + fn compute_path<'a>(&'a self, parent: u64, name: &'a OsStr) -> Result, i32> { + // Get the path from the ino otherwise it is not a known + // path (this means the other methods have to be hit first) + let path = match self.reverse_ino(parent) { + Ok(a) => a, + Err(err) => { + tracing::trace!("fs::compute_path reverse_ino({parent}) errno={err}"); + return Err(err); + } + }; + + // Add the name as a postfix + let name = name.to_string_lossy(); + let path = if path.ends_with("/") { + path + name + } else { + path + "/" + name + }; + Ok(path) + } +} + +impl Filesystem for JournalFileSystem { + fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { + let path = match self.compute_path(parent, name) { + Ok(a) => a, + Err(err) => return reply.error(err), + }; + + match self.attr(path) { + Ok(meta) => reply.entry(&time::Timespec::new(1, 0), &meta, 0), + Err(err) => reply.error(err), + } + } + + fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) { + let path = match self.reverse_ino(ino) { + Ok(a) => a, + Err(err) => { + tracing::trace!("fs::getattr reverse_ino({ino}) errno={err}"); + reply.error(err); + return; + } + }; + + match self.attr(path) { + Ok(meta) => reply.attr(&time::Timespec::new(1, 0), &meta), + Err(err) => reply.error(err), + } + } + + fn open(&mut self, _req: &Request, ino: u64, flags: u32, reply: ReplyOpen) { + let path = match self.reverse_ino(ino) { + Ok(a) => a, + Err(err) => { + tracing::trace!("fs::open reverse_ino({ino}) errno={err}"); + reply.error(err); + return; + } + }; + + let mut state = self.state.inner.lock().unwrap(); + let file = state + .mem_fs + .new_open_options() + .write(true) + .read(true) + .open(&Path::new(path.as_ref())); + let file = match file { + Ok(a) => a, + Err(FsError::EntryNotFound) => { + tracing::trace!("fs::open new_open_options({}) err=ENOENT", path); + reply.error(libc::ENOENT); + return; + } + Err(err) => { + tracing::trace!("fs::open new_open_options({}) err={}", path, err); + reply.error(libc::EIO); + return; + } + }; + + let fh = state.seed.next_val(); + state + .journal_lookup + .insert(fh, Arc::new(tokio::sync::Mutex::new(file))); + + // Write the journals + let entry = JournalEntry::OpenFileDescriptorV1 { + fd: fh, + dirfd: VIRTUAL_ROOT_FD, + dirflags: 0, + path, + o_flags: wasi::Oflags::empty(), + fs_rights_base: wasi::Rights::all(), + fs_rights_inheriting: wasi::Rights::all(), + fs_flags: wasi::Fdflags::empty(), + }; + self.state.write(entry.clone()); + self.journal.write(entry); + + reply.opened(fh as u64, flags); + } + + fn create( + &mut self, + _req: &Request, + parent: u64, + name: &OsStr, + mode: u32, + flags: u32, + reply: ReplyCreate, + ) { + let path = match self.compute_path(parent, name) { + Ok(a) => a, + Err(err) => return reply.error(err), + }; + + // The ino is just the hash of the name + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + path.hash(&mut hasher); + let ino = hasher.finish(); + + // Now create the new file + let mut state = self.state.inner.lock().unwrap(); + let file = state + .mem_fs + .new_open_options() + .create(true) + .write(true) + .read(true) + .open(&Path::new(path.as_ref())); + let file = match file { + Ok(a) => a, + Err(FsError::EntryNotFound) => { + tracing::trace!("fs::create new_open_options({}) err=ENOENT", path); + reply.error(libc::ENOENT); + return; + } + Err(err) => { + tracing::trace!("fs::create new_open_options({}) err={}", path, err); + reply.error(libc::EIO); + return; + } + }; + + // Create the file and load it into the lookup + let fh = state.seed.next_val(); + state + .journal_lookup + .insert(fh, Arc::new(tokio::sync::Mutex::new(file))); + + // Write the journals + let entry = JournalEntry::OpenFileDescriptorV1 { + fd: fh, + dirfd: VIRTUAL_ROOT_FD, + dirflags: 0, + path, + o_flags: wasi::Oflags::empty(), + fs_rights_base: wasi::Rights::all(), + fs_rights_inheriting: wasi::Rights::all(), + fs_flags: wasi::Fdflags::empty(), + }; + self.state.write(entry.clone()); + self.journal.write(entry); + + let now = time::get_time(); + reply.created( + &time::Timespec::new(1, 0), + &FileAttr { + ino, + size: 0, + blocks: 0, + atime: now, + mtime: now, + ctime: now, + crtime: now, + kind: fuse::FileType::RegularFile, + perm: 0o644, + nlink: 1, + uid: 0, + gid: 0, + rdev: 0, + flags: 0, + }, + 0, + fh as u64, + flags, + ); + } + + fn read( + &mut self, + _req: &Request, + ino: u64, + fh: u64, + offset: i64, + size: u32, + reply: ReplyData, + ) { + // Grab the file from the file handle + let mut state = self.state.inner.lock().unwrap(); + let file = match state.fuse_lookup.get_mut(&fh) { + Some(a) => a, + None => { + tracing::trace!("fs::read lookup(fh={fh}) noent err=EIO"); + reply.error(libc::ENOENT); + return; + } + }; + + // Read the data from the file and return it + let data: Result<_, io::Error> = self.handle.block_on(async { + let mut buf = Vec::with_capacity(size as usize); + unsafe { buf.set_len(size as usize) }; + file.seek(io::SeekFrom::Start(offset as u64)).await?; + let amt = file.read(&mut buf).await?; + unsafe { buf.set_len(amt) }; + Ok(buf) + }); + let data = match data { + Ok(a) => a, + Err(err) => { + tracing::trace!("fs::read data err=EIO"); + reply.error(libc::EIO); + return; + } + }; + + // Return the data + reply.data(&data); + } + + fn readdir( + &mut self, + _req: &Request, + ino: u64, + _fh: u64, + offset: i64, + mut reply: ReplyDirectory, + ) { + // Get the path from the ino otherwise it is not a known + // path (this means the other methods have to be hit first) + let path = match self.reverse_ino(ino) { + Ok(a) => a, + Err(err) => { + tracing::trace!("fs::readir reverse_ino({ino}) err={}", err); + reply.error(err); + return; + } + }; + + let mut state = self.state.inner.lock().unwrap(); + let read_dir = state.mem_fs.read_dir(&Path::new(path.as_ref())); + let read_dir = match read_dir { + Ok(a) => a, + Err(FsError::EntryNotFound) => { + tracing::trace!("fs::readir read_dir({}) err=ENOENT", path); + return; + } + Err(err) => { + tracing::trace!("fs::readir read_dir({}) err={}", path, err); + reply.error(libc::EIO); + return; + } + }; + + for (i, entry) in read_dir.into_iter().enumerate().skip(offset as usize) { + let entry = match entry { + Ok(a) => a, + Err(err) => { + tracing::trace!("fs::readir direntry(index={i}) err={}", err); + reply.error(libc::EIO); + return; + } + }; + let path = entry.path.to_string_lossy(); + let name = match entry.path.file_name() { + Some(n) => n, + None => { + tracing::trace!("fs::readir file_name err=EIO"); + reply.error(libc::EIO); + return; + } + }; + + // The ino is just the hash of the name + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + path.hash(&mut hasher); + let ino = hasher.finish(); + state + .inos + .entry(ino) + .or_insert_with(|| path.into_owned().into()); + + // Compute the directory kind + let kind = match entry.file_type() { + Ok(ft) => file_type_to_kind(ft), + _ => fuse::FileType::RegularFile, + }; + + // i + 1 means the index of the next entry + reply.add(ino, (i + 1) as i64, kind, name); + } + reply.ok(); + } + + fn mkdir(&mut self, _req: &Request, parent: u64, name: &OsStr, _mode: u32, reply: ReplyEntry) { + let path = match self.compute_path(parent, name) { + Ok(a) => a, + Err(err) => return reply.error(err), + }; + + let entry = JournalEntry::CreateDirectoryV1 { + fd: VIRTUAL_ROOT_FD, + path: path.clone(), + }; + self.state.write(entry.clone()); + self.journal.write(entry); + + match self.attr(path) { + Ok(meta) => reply.entry(&time::Timespec::new(1, 0), &meta, 0), + Err(err) => reply.error(err), + } + } + + fn rmdir(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { + let path = match self.compute_path(parent, name) { + Ok(a) => a, + Err(err) => return reply.error(err), + }; + + let entry = JournalEntry::RemoveDirectoryV1 { + fd: VIRTUAL_ROOT_FD, + path: path.clone(), + }; + self.state.write(entry.clone()); + self.journal.write(entry); + reply.ok(); + } + + fn unlink(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { + let path = match self.compute_path(parent, name) { + Ok(a) => a, + Err(err) => return reply.error(err), + }; + + let entry = JournalEntry::UnlinkFileV1 { + fd: VIRTUAL_ROOT_FD, + path: path.clone(), + }; + self.state.write(entry.clone()); + self.journal.write(entry); + reply.ok(); + } +} + +fn file_type_to_kind(ft: virtual_fs::FileType) -> fuse::FileType { + if ft.dir { + fuse::FileType::Directory + } else if ft.symlink { + fuse::FileType::Symlink + } else if ft.block_device { + fuse::FileType::BlockDevice + } else if ft.char_device { + fuse::FileType::CharDevice + } else if ft.socket { + fuse::FileType::Socket + } else { + fuse::FileType::RegularFile + } +} diff --git a/lib/cli/src/commands/journal/mount/mod.rs b/lib/cli/src/commands/journal/mount/mod.rs new file mode 100644 index 00000000000..44c28850b14 --- /dev/null +++ b/lib/cli/src/commands/journal/mount/mod.rs @@ -0,0 +1,4 @@ +mod cmd; +mod fs; + +pub use cmd::*; diff --git a/lib/journal/src/base64.rs b/lib/journal/src/base64.rs index 50d251f8329..be558dd6127 100644 --- a/lib/journal/src/base64.rs +++ b/lib/journal/src/base64.rs @@ -14,7 +14,8 @@ pub fn serialize(v: &[u8], s: S) -> Result { pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result, D::Error> { let base64 = String::deserialize(d)?; #[allow(deprecated)] - base64::decode(decompress_size_prepended(base64.as_bytes()).map_err(serde::de::Error::custom)?) + let bytes = base64::decode(base64).map_err(serde::de::Error::custom)?; + decompress_size_prepended(&bytes) .map_err(serde::de::Error::custom) .map(|d| d.into()) } diff --git a/lib/journal/src/concrete/archived.rs b/lib/journal/src/concrete/archived.rs index 3f694da12bf..9a3af4ee87e 100644 --- a/lib/journal/src/concrete/archived.rs +++ b/lib/journal/src/concrete/archived.rs @@ -1,7 +1,9 @@ use lz4_flex::block::compress_prepend_size; use num_enum::{IntoPrimitive, TryFromPrimitive}; use rkyv::ser::{ScratchSpace, Serializer}; -use rkyv::{Archive, CheckBytes, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; +use rkyv::{ + AlignedVec, Archive, CheckBytes, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize, +}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; use std::time::{Duration, SystemTime}; @@ -425,458 +427,477 @@ impl<'a> JournalEntry<'a> { pub fn serialize_archive( self, serializer: &mut T, - ) -> anyhow::Result<()> + ) -> anyhow::Result where T::Error: std::fmt::Display, { - let padding = |size: usize| { - let padding = size % 16; - let padding = match padding { - 0 => 0, - a => 16 - a, - }; - vec![0u8; padding] - }; - match self { - JournalEntry::InitModuleV1 { wasm_hash } => { - serializer.serialize_value(&JournalEntryInitModuleV1 { wasm_hash }) - } - JournalEntry::UpdateMemoryRegionV1 { region, data } => { - serializer.serialize_value(&JournalEntryUpdateMemoryRegionV1 { - start: region.start, - end: region.end, - _padding: padding(data.len()), - compressed_data: compress_prepend_size(data.as_ref()), - }) - } - JournalEntry::ProcessExitV1 { exit_code } => { - serializer.serialize_value(&JournalEntryProcessExitV1 { - exit_code: exit_code.map(|e| e.into()), - _padding: 0, - }) - } - JournalEntry::SetThreadV1 { - id, - call_stack, - memory_stack, - store_data, - is_64bit, - } => serializer.serialize_value(&JournalEntrySetThreadV1 { - id, - _padding: padding(call_stack.len() + memory_stack.len() + store_data.len()), - call_stack: call_stack.into_owned(), - memory_stack: memory_stack.into_owned(), - store_data: store_data.into_owned(), - is_64bit, - }), - JournalEntry::CloseThreadV1 { id, exit_code } => { - serializer.serialize_value(&JournalEntryCloseThreadV1 { + let amt = + match self { + JournalEntry::InitModuleV1 { wasm_hash } => { + serializer.serialize_value(&JournalEntryInitModuleV1 { wasm_hash }) + } + JournalEntry::UpdateMemoryRegionV1 { region, data } => { + serializer.serialize_value(&JournalEntryUpdateMemoryRegionV1 { + start: region.start, + end: region.end, + compressed_data: to_aligned_vec(compress_prepend_size(data.as_ref())), + }) + } + JournalEntry::ProcessExitV1 { exit_code } => { + serializer.serialize_value(&JournalEntryProcessExitV1 { + exit_code: exit_code.map(|e| e.into()), + _padding: 0, + }) + } + JournalEntry::SetThreadV1 { id, - exit_code: exit_code.map(|e| e.into()), - }) - } - JournalEntry::FileDescriptorSeekV1 { fd, offset, whence } => serializer - .serialize_value(&JournalEntryFileDescriptorSeekV1 { + call_stack, + memory_stack, + store_data, + is_64bit, + } => serializer.serialize_value(&JournalEntrySetThreadV1 { + id, + call_stack: to_aligned_vec(call_stack), + memory_stack: to_aligned_vec(memory_stack), + store_data: to_aligned_vec(store_data), + is_64bit, + }), + JournalEntry::CloseThreadV1 { id, exit_code } => { + serializer.serialize_value(&JournalEntryCloseThreadV1 { + id, + exit_code: exit_code.map(|e| e.into()), + }) + } + JournalEntry::FileDescriptorSeekV1 { fd, offset, whence } => serializer + .serialize_value(&JournalEntryFileDescriptorSeekV1 { + fd, + offset, + whence: whence.into(), + }), + JournalEntry::FileDescriptorWriteV1 { + fd, + offset, + data, + is_64bit, + } => serializer.serialize_value(&JournalEntryFileDescriptorWriteV1 { fd, offset, - whence: whence.into(), + data: to_aligned_vec(data), + is_64bit, }), - JournalEntry::FileDescriptorWriteV1 { - fd, - offset, - data, - is_64bit, - } => serializer.serialize_value(&JournalEntryFileDescriptorWriteV1 { - fd, - offset, - _padding: padding(data.len()), - data: data.into_owned(), - is_64bit, - }), - JournalEntry::SetClockTimeV1 { clock_id, time } => { - serializer.serialize_value(&JournalEntrySetClockTimeV1 { - clock_id: clock_id.into(), - time, - }) - } - JournalEntry::CloseFileDescriptorV1 { fd } => { - serializer.serialize_value(&JournalEntryCloseFileDescriptorV1 { fd, _padding: 0 }) - } - JournalEntry::OpenFileDescriptorV1 { - fd, - dirfd, - dirflags, - path, - o_flags, - fs_rights_base, - fs_rights_inheriting, - fs_flags, - } => serializer.serialize_value(&JournalEntryOpenFileDescriptorV1 { - fd, - dirfd, - dirflags, - _padding: padding(path.as_bytes().len()), - path: path.into_owned(), - o_flags: o_flags.bits(), - fs_rights_base: fs_rights_base.bits(), - fs_rights_inheriting: fs_rights_inheriting.bits(), - fs_flags: fs_flags.bits(), - }), - JournalEntry::RenumberFileDescriptorV1 { old_fd, new_fd } => { - serializer.serialize_value(&JournalEntryRenumberFileDescriptorV1 { old_fd, new_fd }) - } - JournalEntry::DuplicateFileDescriptorV1 { - original_fd, - copied_fd, - } => serializer.serialize_value(&JournalEntryDuplicateFileDescriptorV1 { - original_fd, - copied_fd, - }), - JournalEntry::CreateDirectoryV1 { fd, path } => { - serializer.serialize_value(&JournalEntryCreateDirectoryV1 { + JournalEntry::SetClockTimeV1 { clock_id, time } => { + serializer.serialize_value(&JournalEntrySetClockTimeV1 { + clock_id: clock_id.into(), + time, + }) + } + JournalEntry::CloseFileDescriptorV1 { fd } => serializer + .serialize_value(&JournalEntryCloseFileDescriptorV1 { fd, _padding: 0 }), + JournalEntry::OpenFileDescriptorV1 { fd, - _padding: padding(path.as_bytes().len()), - path: path.into_owned(), - }) - } - JournalEntry::RemoveDirectoryV1 { fd, path } => { - serializer.serialize_value(&JournalEntryRemoveDirectoryV1 { + dirfd, + dirflags, + path, + o_flags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + } => serializer.serialize_value(&JournalEntryOpenFileDescriptorV1 { fd, - _padding: padding(path.as_bytes().len()), - path: path.into_owned(), - }) - } - JournalEntry::PathSetTimesV1 { - fd, - flags, - path, - st_atim, - st_mtim, - fst_flags, - } => serializer.serialize_value(&JournalEntryPathSetTimesV1 { - fd, - flags, - _padding: padding(path.as_bytes().len()), - path: path.into_owned(), - st_atim, - st_mtim, - fst_flags: fst_flags.bits(), - }), - JournalEntry::FileDescriptorSetTimesV1 { - fd, - st_atim, - st_mtim, - fst_flags, - } => serializer.serialize_value(&JournalEntryFileDescriptorSetTimesV1 { - fd, - st_atim, - st_mtim, - fst_flags: fst_flags.bits(), - }), - JournalEntry::FileDescriptorSetFlagsV1 { fd, flags } => { - serializer.serialize_value(&JournalEntryFileDescriptorSetFlagsV1 { + dirfd, + dirflags, + path: to_aligned_vec(path.as_bytes()), + o_flags: o_flags.bits(), + fs_rights_base: fs_rights_base.bits(), + fs_rights_inheriting: fs_rights_inheriting.bits(), + fs_flags: fs_flags.bits(), + }), + JournalEntry::RenumberFileDescriptorV1 { old_fd, new_fd } => serializer + .serialize_value(&JournalEntryRenumberFileDescriptorV1 { old_fd, new_fd }), + JournalEntry::DuplicateFileDescriptorV1 { + original_fd, + copied_fd, + } => serializer.serialize_value(&JournalEntryDuplicateFileDescriptorV1 { + original_fd, + copied_fd, + }), + JournalEntry::CreateDirectoryV1 { fd, path } => { + serializer.serialize_value(&JournalEntryCreateDirectoryV1 { + fd, + path: to_aligned_vec(path.as_bytes()), + _padding: 0, + }) + } + JournalEntry::RemoveDirectoryV1 { fd, path } => { + serializer.serialize_value(&JournalEntryRemoveDirectoryV1 { + fd, + path: to_aligned_vec(path.as_bytes()), + _padding: 0, + }) + } + JournalEntry::PathSetTimesV1 { fd, - flags: flags.bits(), - }) - } - JournalEntry::FileDescriptorSetRightsV1 { - fd, - fs_rights_base, - fs_rights_inheriting, - } => serializer.serialize_value(&JournalEntryFileDescriptorSetRightsV1 { - fd, - fs_rights_base: fs_rights_base.bits(), - fs_rights_inheriting: fs_rights_inheriting.bits(), - }), - JournalEntry::FileDescriptorSetSizeV1 { fd, st_size } => { - serializer.serialize_value(&JournalEntryFileDescriptorSetSizeV1 { fd, st_size }) - } - JournalEntry::FileDescriptorAdviseV1 { - fd, - offset, - len, - advice, - } => serializer.serialize_value(&JournalEntryFileDescriptorAdviseV1 { - fd, - offset, - len, - advice: advice.into(), - }), - JournalEntry::FileDescriptorAllocateV1 { fd, offset, len } => serializer - .serialize_value(&JournalEntryFileDescriptorAllocateV1 { fd, offset, len }), - JournalEntry::CreateHardLinkV1 { - old_fd, - old_path, - old_flags, - new_fd, - new_path, - } => serializer.serialize_value(&JournalEntryCreateHardLinkV1 { - old_fd, - _padding: padding(old_path.as_bytes().len() + new_path.as_bytes().len()), - old_path: old_path.into_owned(), - old_flags, - new_fd, - new_path: new_path.into_owned(), - }), - JournalEntry::CreateSymbolicLinkV1 { - old_path, - fd, - new_path, - } => serializer.serialize_value(&JournalEntryCreateSymbolicLinkV1 { - _padding: padding(old_path.as_bytes().len() + new_path.as_bytes().len()), - old_path: old_path.into_owned(), - fd, - new_path: new_path.into_owned(), - }), - JournalEntry::UnlinkFileV1 { fd, path } => { - serializer.serialize_value(&JournalEntryUnlinkFileV1 { + flags, + path, + st_atim, + st_mtim, + fst_flags, + } => serializer.serialize_value(&JournalEntryPathSetTimesV1 { fd, - _padding: padding(path.as_bytes().len()), - path: path.into_owned(), - }) - } - JournalEntry::PathRenameV1 { - old_fd, - old_path, - new_fd, - new_path, - } => serializer.serialize_value(&JournalEntryPathRenameV1 { - old_fd, - _padding: padding(old_path.as_bytes().len() + new_path.as_bytes().len()), - old_path: old_path.into_owned(), - new_fd, - new_path: new_path.into_owned(), - }), - JournalEntry::ChangeDirectoryV1 { path } => { - serializer.serialize_value(&JournalEntryChangeDirectoryV1 { - path: path.into_owned(), - }) - } - JournalEntry::EpollCreateV1 { fd } => { - serializer.serialize_value(&JournalEntryEpollCreateV1 { fd, _padding: 0 }) - } - JournalEntry::EpollCtlV1 { - epfd, - op, - fd, - event, - } => serializer.serialize_value(&JournalEntryEpollCtlV1 { - epfd, - op: op.into(), - fd, - event: event.map(|e| e.into()), - }), - JournalEntry::TtySetV1 { tty, line_feeds } => { - serializer.serialize_value(&JournalEntryTtySetV1 { - cols: tty.cols, - rows: tty.rows, - width: tty.width, - height: tty.height, - stdin_tty: tty.stdin_tty, - stdout_tty: tty.stdout_tty, - stderr_tty: tty.stderr_tty, - echo: tty.echo, - line_buffered: tty.line_buffered, - line_feeds, - }) - } - JournalEntry::CreatePipeV1 { fd1, fd2 } => { - serializer.serialize_value(&JournalEntryCreatePipeV1 { fd1, fd2 }) - } - JournalEntry::CreateEventV1 { - initial_val, - flags, - fd, - } => serializer.serialize_value(&JournalEntryCreateEventV1 { - initial_val, - flags, - fd, - }), - JournalEntry::PortAddAddrV1 { cidr } => { - serializer.serialize_value(&JournalEntryPortAddAddrV1 { cidr: cidr.into() }) - } - JournalEntry::PortDelAddrV1 { addr } => { - serializer.serialize_value(&JournalEntryPortDelAddrV1 { addr }) - } - JournalEntry::PortAddrClearV1 => return Ok(()), - JournalEntry::PortBridgeV1 { - network, - token, - security, - } => serializer.serialize_value(&JournalEntryPortBridgeV1 { - _padding: padding(network.as_bytes().len() + token.as_bytes().len()), - network: network.into_owned(), - token: token.into_owned(), - security: security.into(), - }), - JournalEntry::PortUnbridgeV1 => return Ok(()), - JournalEntry::PortDhcpAcquireV1 => return Ok(()), - JournalEntry::PortGatewaySetV1 { ip } => { - serializer.serialize_value(&JournalEntryPortGatewaySetV1 { ip }) - } - JournalEntry::PortRouteAddV1 { - cidr, - via_router, - preferred_until, - expires_at, - } => serializer.serialize_value(&JournalEntryPortRouteAddV1 { - cidr: cidr.into(), - via_router, - preferred_until, - expires_at, - }), - JournalEntry::PortRouteClearV1 => return Ok(()), - JournalEntry::PortRouteDelV1 { ip } => { - serializer.serialize_value(&JournalEntryPortRouteDelV1 { ip }) - } - JournalEntry::SocketOpenV1 { af, ty, pt, fd } => { - serializer.serialize_value(&JournalEntrySocketOpenV1 { - af: af.into(), - ty: ty.into(), - pt: pt.into(), + flags, + path: to_aligned_vec(path.as_bytes()), + st_atim, + st_mtim, + fst_flags: fst_flags.bits(), + _padding1: 0, + _padding2: 0, + }), + JournalEntry::FileDescriptorSetTimesV1 { fd, - }) - } - JournalEntry::SocketListenV1 { fd, backlog } => { - serializer.serialize_value(&JournalEntrySocketListenV1 { fd, backlog }) - } - JournalEntry::SocketBindV1 { fd, addr } => { - serializer.serialize_value(&JournalEntrySocketBindV1 { + st_atim, + st_mtim, + fst_flags, + } => serializer.serialize_value(&JournalEntryFileDescriptorSetTimesV1 { fd, - addr, + st_atim, + st_mtim, + fst_flags: fst_flags.bits(), _padding: 0, - }) - } - JournalEntry::SocketConnectedV1 { fd, addr } => { - serializer.serialize_value(&JournalEntrySocketConnectedV1 { fd, addr }) - } - JournalEntry::SocketAcceptedV1 { - listen_fd, - fd, - peer_addr, - fd_flags, - non_blocking: nonblocking, - } => serializer.serialize_value(&JournalEntrySocketAcceptedV1 { - listen_fd, - fd, - peer_addr, - fd_flags: fd_flags.bits(), - nonblocking, - }), - JournalEntry::SocketJoinIpv4MulticastV1 { - fd, - multiaddr, - iface, - } => serializer.serialize_value(&JournalEntrySocketJoinIpv4MulticastV1 { - fd, - multiaddr, - iface, - }), - JournalEntry::SocketJoinIpv6MulticastV1 { - fd, - multi_addr: multiaddr, - iface, - } => serializer.serialize_value(&JournalEntrySocketJoinIpv6MulticastV1 { - fd, - multiaddr, - iface, - }), - JournalEntry::SocketLeaveIpv4MulticastV1 { - fd, - multi_addr: multiaddr, - iface, - } => serializer.serialize_value(&JournalEntrySocketLeaveIpv4MulticastV1 { - fd, - multiaddr, - iface, - }), - JournalEntry::SocketLeaveIpv6MulticastV1 { - fd, - multi_addr: multiaddr, - iface, - } => serializer.serialize_value(&JournalEntrySocketLeaveIpv6MulticastV1 { - fd, - multiaddr, - iface, - }), - JournalEntry::SocketSendFileV1 { - socket_fd, - file_fd, - offset, - count, - } => serializer.serialize_value(&JournalEntrySocketSendFileV1 { - socket_fd, - file_fd, - offset, - count, - }), - JournalEntry::SocketSendToV1 { - fd, - data, - flags, - addr, - is_64bit, - } => serializer.serialize_value(&JournalEntrySocketSendToV1 { - fd, - _padding: padding(data.len()), - data: data.into_owned(), - flags, - addr, - is_64bit, - }), - JournalEntry::SocketSendV1 { - fd, - data, - flags, - is_64bit, - } => serializer.serialize_value(&JournalEntrySocketSendV1 { - fd, - _padding: padding(data.len()), - data: data.into_owned(), - flags, - is_64bit, - }), - JournalEntry::SocketSetOptFlagV1 { fd, opt, flag } => { - serializer.serialize_value(&JournalEntrySocketSetOptFlagV1 { + }), + JournalEntry::FileDescriptorSetFlagsV1 { fd, flags } => { + serializer.serialize_value(&JournalEntryFileDescriptorSetFlagsV1 { + fd, + flags: flags.bits(), + _padding: 0, + }) + } + JournalEntry::FileDescriptorSetRightsV1 { fd, - opt: opt.into(), - flag, - }) - } - JournalEntry::SocketSetOptSizeV1 { fd, opt, size } => { - serializer.serialize_value(&JournalEntrySocketSetOptSizeV1 { + fs_rights_base, + fs_rights_inheriting, + } => serializer.serialize_value(&JournalEntryFileDescriptorSetRightsV1 { fd, - opt: opt.into(), - size, - }) - } - JournalEntry::SocketSetOptTimeV1 { fd, ty, time } => { - serializer.serialize_value(&JournalEntrySocketSetOptTimeV1 { + fs_rights_base: fs_rights_base.bits(), + fs_rights_inheriting: fs_rights_inheriting.bits(), + }), + JournalEntry::FileDescriptorSetSizeV1 { fd, st_size } => serializer + .serialize_value(&JournalEntryFileDescriptorSetSizeV1 { + fd, + st_size, + _padding: 0, + }), + JournalEntry::FileDescriptorAdviseV1 { fd, - ty: ty.into(), - time, - }) - } - JournalEntry::SocketShutdownV1 { fd, how } => { - serializer.serialize_value(&JournalEntrySocketShutdownV1 { + offset, + len, + advice, + } => serializer.serialize_value(&JournalEntryFileDescriptorAdviseV1 { fd, - how: how.into(), - }) - } - JournalEntry::SnapshotV1 { when, trigger } => { - serializer.serialize_value(&JournalEntrySnapshotV1 { - since_epoch: when - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap_or(Duration::ZERO), - trigger: trigger.into(), - }) - } - } - .map_err(|err| anyhow::format_err!("failed to serialize journal record - {}", err))?; - Ok(()) + offset, + len, + advice: advice.into(), + }), + JournalEntry::FileDescriptorAllocateV1 { fd, offset, len } => serializer + .serialize_value(&JournalEntryFileDescriptorAllocateV1 { + fd, + offset, + len, + _padding: 0, + }), + JournalEntry::CreateHardLinkV1 { + old_fd, + old_path, + old_flags, + new_fd, + new_path, + } => serializer.serialize_value(&JournalEntryCreateHardLinkV1 { + old_fd, + old_path: to_aligned_vec(old_path.as_bytes()), + old_flags, + new_fd, + new_path: to_aligned_vec(new_path.as_bytes()), + _padding: 0, + }), + JournalEntry::CreateSymbolicLinkV1 { + old_path, + fd, + new_path, + } => serializer.serialize_value(&JournalEntryCreateSymbolicLinkV1 { + old_path: to_aligned_vec(old_path.as_bytes()), + fd, + _padding: 0, + new_path: to_aligned_vec(new_path.as_bytes()), + }), + JournalEntry::UnlinkFileV1 { fd, path } => { + serializer.serialize_value(&JournalEntryUnlinkFileV1 { + fd, + path: to_aligned_vec(path.as_bytes()), + _padding: 0, + }) + } + JournalEntry::PathRenameV1 { + old_fd, + old_path, + new_fd, + new_path, + } => serializer.serialize_value(&JournalEntryPathRenameV1 { + old_fd, + old_path: to_aligned_vec_str(old_path), + new_fd, + new_path: to_aligned_vec_str(new_path), + }), + JournalEntry::ChangeDirectoryV1 { path } => { + serializer.serialize_value(&JournalEntryChangeDirectoryV1 { + path: to_aligned_vec_str(path), + }) + } + JournalEntry::EpollCreateV1 { fd } => { + serializer.serialize_value(&JournalEntryEpollCreateV1 { fd, _padding: 0 }) + } + JournalEntry::EpollCtlV1 { + epfd, + op, + fd, + event, + } => serializer.serialize_value(&JournalEntryEpollCtlV1 { + epfd, + op: op.into(), + fd, + event: event.map(|e| e.into()), + }), + JournalEntry::TtySetV1 { tty, line_feeds } => { + serializer.serialize_value(&JournalEntryTtySetV1 { + cols: tty.cols, + rows: tty.rows, + width: tty.width, + height: tty.height, + stdin_tty: tty.stdin_tty, + stdout_tty: tty.stdout_tty, + stderr_tty: tty.stderr_tty, + echo: tty.echo, + line_buffered: tty.line_buffered, + line_feeds, + }) + } + JournalEntry::CreatePipeV1 { fd1, fd2 } => { + serializer.serialize_value(&JournalEntryCreatePipeV1 { fd1, fd2 }) + } + JournalEntry::CreateEventV1 { + initial_val, + flags, + fd, + } => serializer.serialize_value(&JournalEntryCreateEventV1 { + initial_val, + flags, + fd, + _padding: 0, + }), + JournalEntry::PortAddAddrV1 { cidr } => { + serializer.serialize_value(&JournalEntryPortAddAddrV1 { cidr: cidr.into() }) + } + JournalEntry::PortDelAddrV1 { addr } => { + serializer.serialize_value(&JournalEntryPortDelAddrV1 { addr }) + } + JournalEntry::PortAddrClearV1 => serializer.serialize_value(&()), + JournalEntry::PortBridgeV1 { + network, + token, + security, + } => serializer.serialize_value(&JournalEntryPortBridgeV1 { + network: to_aligned_vec_str(network), + token: to_aligned_vec_str(token), + security: security.into(), + }), + JournalEntry::PortUnbridgeV1 => serializer.serialize_value(&()), + JournalEntry::PortDhcpAcquireV1 => serializer.serialize_value(&()), + JournalEntry::PortGatewaySetV1 { ip } => { + serializer.serialize_value(&JournalEntryPortGatewaySetV1 { ip }) + } + JournalEntry::PortRouteAddV1 { + cidr, + via_router, + preferred_until, + expires_at, + } => serializer.serialize_value(&JournalEntryPortRouteAddV1 { + cidr: cidr.into(), + via_router, + preferred_until, + expires_at, + }), + JournalEntry::PortRouteClearV1 => serializer.serialize_value(&()), + JournalEntry::PortRouteDelV1 { ip } => { + serializer.serialize_value(&JournalEntryPortRouteDelV1 { ip }) + } + JournalEntry::SocketOpenV1 { af, ty, pt, fd } => { + serializer.serialize_value(&JournalEntrySocketOpenV1 { + af: af.into(), + ty: ty.into(), + pt: pt.into(), + fd, + }) + } + JournalEntry::SocketListenV1 { fd, backlog } => { + serializer.serialize_value(&JournalEntrySocketListenV1 { fd, backlog }) + } + JournalEntry::SocketBindV1 { fd, addr } => { + serializer.serialize_value(&JournalEntrySocketBindV1 { + fd, + addr, + _padding: 0, + }) + } + JournalEntry::SocketConnectedV1 { fd, addr } => { + serializer.serialize_value(&JournalEntrySocketConnectedV1 { + fd, + addr, + _padding: 0, + }) + } + JournalEntry::SocketAcceptedV1 { + listen_fd, + fd, + peer_addr, + fd_flags, + non_blocking: nonblocking, + } => serializer.serialize_value(&JournalEntrySocketAcceptedV1 { + listen_fd, + fd, + peer_addr, + fd_flags: fd_flags.bits(), + nonblocking, + }), + JournalEntry::SocketJoinIpv4MulticastV1 { + fd, + multiaddr, + iface, + } => serializer.serialize_value(&JournalEntrySocketJoinIpv4MulticastV1 { + fd, + multiaddr, + iface, + }), + JournalEntry::SocketJoinIpv6MulticastV1 { + fd, + multi_addr: multiaddr, + iface, + } => serializer.serialize_value(&JournalEntrySocketJoinIpv6MulticastV1 { + fd, + multiaddr, + iface, + }), + JournalEntry::SocketLeaveIpv4MulticastV1 { + fd, + multi_addr: multiaddr, + iface, + } => serializer.serialize_value(&JournalEntrySocketLeaveIpv4MulticastV1 { + fd, + multiaddr, + iface, + }), + JournalEntry::SocketLeaveIpv6MulticastV1 { + fd, + multi_addr: multiaddr, + iface, + } => serializer.serialize_value(&JournalEntrySocketLeaveIpv6MulticastV1 { + fd, + multiaddr, + iface, + }), + JournalEntry::SocketSendFileV1 { + socket_fd, + file_fd, + offset, + count, + } => serializer.serialize_value(&JournalEntrySocketSendFileV1 { + socket_fd, + file_fd, + offset, + count, + }), + JournalEntry::SocketSendToV1 { + fd, + data, + flags, + addr, + is_64bit, + } => serializer.serialize_value(&JournalEntrySocketSendToV1 { + fd, + data: to_aligned_vec(data), + flags, + addr, + is_64bit, + }), + JournalEntry::SocketSendV1 { + fd, + data, + flags, + is_64bit, + } => serializer.serialize_value(&JournalEntrySocketSendV1 { + fd, + data: to_aligned_vec(data), + flags, + is_64bit, + }), + JournalEntry::SocketSetOptFlagV1 { fd, opt, flag } => { + serializer.serialize_value(&JournalEntrySocketSetOptFlagV1 { + fd, + opt: opt.into(), + flag, + }) + } + JournalEntry::SocketSetOptSizeV1 { fd, opt, size } => { + serializer.serialize_value(&JournalEntrySocketSetOptSizeV1 { + fd, + opt: opt.into(), + size, + }) + } + JournalEntry::SocketSetOptTimeV1 { fd, ty, time } => { + serializer.serialize_value(&JournalEntrySocketSetOptTimeV1 { + fd, + ty: ty.into(), + time, + }) + } + JournalEntry::SocketShutdownV1 { fd, how } => { + serializer.serialize_value(&JournalEntrySocketShutdownV1 { + fd, + how: how.into(), + }) + } + JournalEntry::SnapshotV1 { when, trigger } => { + serializer.serialize_value(&JournalEntrySnapshotV1 { + since_epoch: when + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap_or(Duration::ZERO), + trigger: trigger.into(), + }) + } + } + .map_err(|err| anyhow::format_err!("failed to serialize journal record - {}", err))?; + Ok(amt) } } +fn to_aligned_vec(data: D) -> AlignedVec +where + D: AsRef<[u8]>, +{ + let data = data.as_ref(); + let mut ret = AlignedVec::with_capacity(data.len()); + ret.extend_from_slice(&data); + ret +} + +fn to_aligned_vec_str(data: S) -> AlignedVec +where + S: AsRef, +{ + let data = data.as_ref().as_bytes(); + let mut ret = AlignedVec::with_capacity(data.len()); + ret.extend_from_slice(&data); + ret +} + /// The journal log entries are serializable which /// allows them to be written directly to a file /// @@ -974,10 +995,9 @@ pub struct JournalEntryProcessExitV1 { #[archive_attr(derive(CheckBytes))] pub struct JournalEntrySetThreadV1 { pub id: u32, - pub call_stack: Vec, - pub memory_stack: Vec, - pub store_data: Vec, - pub _padding: Vec, + pub call_stack: AlignedVec, + pub memory_stack: AlignedVec, + pub store_data: AlignedVec, pub is_64bit: bool, } @@ -996,8 +1016,8 @@ pub struct JournalEntryCloseThreadV1 { #[archive_attr(derive(CheckBytes))] pub struct JournalEntryFileDescriptorSeekV1 { pub fd: u32, - pub offset: i64, pub whence: JournalWhenceV1, + pub offset: i64, } #[repr(C)] @@ -1005,10 +1025,9 @@ pub struct JournalEntryFileDescriptorSeekV1 { #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes))] pub struct JournalEntryFileDescriptorWriteV1 { - pub fd: u32, + pub data: AlignedVec, pub offset: u64, - pub data: Vec, - pub _padding: Vec, + pub fd: u32, pub is_64bit: bool, } @@ -1017,10 +1036,9 @@ pub struct JournalEntryFileDescriptorWriteV1 { #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes))] pub struct JournalEntryUpdateMemoryRegionV1 { + pub compressed_data: AlignedVec, pub start: u64, pub end: u64, - pub compressed_data: Vec, - pub _padding: Vec, } #[repr(C)] @@ -1040,12 +1058,11 @@ pub struct JournalEntryOpenFileDescriptorV1 { pub fd: u32, pub dirfd: u32, pub dirflags: u32, - pub path: String, - pub _padding: Vec, + pub fs_flags: u16, pub o_flags: u16, pub fs_rights_base: u64, pub fs_rights_inheriting: u64, - pub fs_flags: u16, + pub path: AlignedVec, } #[repr(C)] @@ -1081,8 +1098,8 @@ pub struct JournalEntryDuplicateFileDescriptorV1 { #[archive_attr(derive(CheckBytes))] pub struct JournalEntryCreateDirectoryV1 { pub fd: u32, - pub path: String, - pub _padding: Vec, + pub _padding: u32, + pub path: AlignedVec, } #[repr(C)] @@ -1091,8 +1108,8 @@ pub struct JournalEntryCreateDirectoryV1 { #[archive_attr(derive(CheckBytes))] pub struct JournalEntryRemoveDirectoryV1 { pub fd: u32, - pub path: String, - pub _padding: Vec, + pub _padding: u32, + pub path: AlignedVec, } #[repr(C)] @@ -1102,11 +1119,12 @@ pub struct JournalEntryRemoveDirectoryV1 { pub struct JournalEntryPathSetTimesV1 { pub fd: u32, pub flags: u32, - pub path: String, - pub _padding: Vec, + pub path: AlignedVec, pub st_atim: u64, pub st_mtim: u64, pub fst_flags: u16, + pub _padding1: u16, + pub _padding2: u32, } #[repr(C)] @@ -1115,9 +1133,10 @@ pub struct JournalEntryPathSetTimesV1 { #[archive_attr(derive(CheckBytes))] pub struct JournalEntryFileDescriptorSetTimesV1 { pub fd: u32, + pub fst_flags: u16, + pub _padding: u16, pub st_atim: u64, pub st_mtim: u64, - pub fst_flags: u16, } #[repr(C)] @@ -1126,6 +1145,7 @@ pub struct JournalEntryFileDescriptorSetTimesV1 { #[archive_attr(derive(CheckBytes))] pub struct JournalEntryFileDescriptorSetSizeV1 { pub fd: u32, + pub _padding: u32, pub st_size: u64, } @@ -1136,6 +1156,7 @@ pub struct JournalEntryFileDescriptorSetSizeV1 { pub struct JournalEntryFileDescriptorSetFlagsV1 { pub fd: u32, pub flags: u16, + pub _padding: u16, } #[repr(C)] @@ -1165,6 +1186,7 @@ pub struct JournalEntryFileDescriptorAdviseV1 { #[archive_attr(derive(CheckBytes))] pub struct JournalEntryFileDescriptorAllocateV1 { pub fd: u32, + pub _padding: u32, pub offset: u64, pub len: u64, } @@ -1175,11 +1197,11 @@ pub struct JournalEntryFileDescriptorAllocateV1 { #[archive_attr(derive(CheckBytes))] pub struct JournalEntryCreateHardLinkV1 { pub old_fd: u32, - pub old_path: String, + pub _padding: u32, + pub old_path: AlignedVec, pub old_flags: u32, pub new_fd: u32, - pub new_path: String, - pub _padding: Vec, + pub new_path: AlignedVec, } #[repr(C)] @@ -1187,10 +1209,10 @@ pub struct JournalEntryCreateHardLinkV1 { #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes))] pub struct JournalEntryCreateSymbolicLinkV1 { - pub old_path: String, pub fd: u32, - pub new_path: String, - pub _padding: Vec, + pub _padding: u32, + pub old_path: AlignedVec, + pub new_path: AlignedVec, } #[repr(C)] @@ -1199,8 +1221,8 @@ pub struct JournalEntryCreateSymbolicLinkV1 { #[archive_attr(derive(CheckBytes))] pub struct JournalEntryUnlinkFileV1 { pub fd: u32, - pub path: String, - pub _padding: Vec, + pub _padding: u32, + pub path: AlignedVec, } #[repr(C)] @@ -1209,10 +1231,9 @@ pub struct JournalEntryUnlinkFileV1 { #[archive_attr(derive(CheckBytes))] pub struct JournalEntryPathRenameV1 { pub old_fd: u32, - pub old_path: String, + pub old_path: AlignedVec, pub new_fd: u32, - pub new_path: String, - pub _padding: Vec, + pub new_path: AlignedVec, } #[repr(C)] @@ -1220,7 +1241,7 @@ pub struct JournalEntryPathRenameV1 { #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes))] pub struct JournalEntryChangeDirectoryV1 { - pub path: String, + pub path: AlignedVec, } #[repr(C)] @@ -1276,6 +1297,7 @@ pub struct JournalEntryCreatePipeV1 { pub struct JournalEntryCreateEventV1 { pub initial_val: u64, pub flags: u16, + pub _padding: u16, pub fd: u32, } @@ -1300,9 +1322,8 @@ pub struct JournalEntryPortDelAddrV1 { #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes))] pub struct JournalEntryPortBridgeV1 { - pub network: String, - pub token: String, - pub _padding: Vec, + pub network: AlignedVec, + pub token: AlignedVec, pub security: JournalStreamSecurityV1, } @@ -1369,6 +1390,7 @@ pub struct JournalEntrySocketBindV1 { #[archive_attr(derive(CheckBytes))] pub struct JournalEntrySocketConnectedV1 { pub fd: u32, + pub _padding: u32, pub addr: SocketAddr, } @@ -1441,8 +1463,7 @@ pub struct JournalEntrySocketSendFileV1 { #[archive_attr(derive(CheckBytes))] pub struct JournalEntrySocketSendToV1 { pub fd: u32, - pub data: Vec, - pub _padding: Vec, + pub data: AlignedVec, pub flags: u16, pub addr: SocketAddr, pub is_64bit: bool, @@ -1454,8 +1475,7 @@ pub struct JournalEntrySocketSendToV1 { #[archive_attr(derive(CheckBytes))] pub struct JournalEntrySocketSendV1 { pub fd: u32, - pub data: Vec, - pub _padding: Vec, + pub data: AlignedVec, pub flags: u16, pub is_64bit: bool, } diff --git a/lib/journal/src/concrete/archived_from.rs b/lib/journal/src/concrete/archived_from.rs index 5b553793473..f5f12c5fffb 100644 --- a/lib/journal/src/concrete/archived_from.rs +++ b/lib/journal/src/concrete/archived_from.rs @@ -584,7 +584,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { start, end, compressed_data, - _padding: _, }, ) => Self::UpdateMemoryRegionV1 { region: (*start)..(*end), @@ -601,7 +600,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { call_stack, memory_stack, store_data, - _padding: _, is_64bit, }) => Self::SetThreadV1 { id: *id, @@ -623,7 +621,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { fd, offset, is_64bit, - _padding: _, }, ) => Self::FileDescriptorWriteV1 { data: data.as_ref().into(), @@ -652,13 +649,12 @@ impl<'a> TryFrom> for JournalEntry<'a> { fs_rights_base, fs_rights_inheriting, fs_flags, - _padding: _, }, ) => Self::OpenFileDescriptorV1 { fd: *fd, dirfd: *dirfd, dirflags: *dirflags, - path: path.as_ref().into(), + path: String::from_utf8_lossy(path.as_ref()), o_flags: wasi::Oflags::from_bits_truncate(*o_flags), fs_rights_base: wasi::Rights::from_bits_truncate(*fs_rights_base), fs_rights_inheriting: wasi::Rights::from_bits_truncate(*fs_rights_inheriting), @@ -673,7 +669,7 @@ impl<'a> TryFrom> for JournalEntry<'a> { _padding: _, }) => Self::RemoveDirectoryV1 { fd: *fd, - path: path.as_ref().into(), + path: String::from_utf8_lossy(path.as_ref()), }, ArchivedJournalEntry::UnlinkFileV1(ArchivedJournalEntryUnlinkFileV1 { fd, @@ -681,19 +677,18 @@ impl<'a> TryFrom> for JournalEntry<'a> { _padding: _, }) => Self::UnlinkFileV1 { fd: *fd, - path: path.as_ref().into(), + path: String::from_utf8_lossy(path.as_ref()), }, ArchivedJournalEntry::PathRenameV1(ArchivedJournalEntryPathRenameV1 { old_fd, old_path, new_fd, new_path, - _padding: _, }) => Self::PathRenameV1 { old_fd: *old_fd, - old_path: old_path.as_ref().into(), + old_path: String::from_utf8_lossy(old_path.as_ref()), new_fd: *new_fd, - new_path: new_path.as_ref().into(), + new_path: String::from_utf8_lossy(new_path.as_ref()), }, ArchivedJournalEntry::SnapshotV1(ArchivedJournalEntrySnapshotV1 { since_epoch, @@ -732,7 +727,7 @@ impl<'a> TryFrom> for JournalEntry<'a> { _padding: _, }) => Self::CreateDirectoryV1 { fd: *fd, - path: path.as_ref().into(), + path: String::from_utf8_lossy(path.as_ref()), }, ArchivedJournalEntry::PathSetTimesV1(ArchivedJournalEntryPathSetTimesV1 { fd, @@ -741,10 +736,11 @@ impl<'a> TryFrom> for JournalEntry<'a> { st_atim, st_mtim, fst_flags, - _padding: _, + _padding1: _, + _padding2: _, }) => Self::PathSetTimesV1 { fd: *fd, - path: path.as_ref().into(), + path: String::from_utf8_lossy(path.as_ref()), flags: *flags, st_atim: *st_atim, st_mtim: *st_mtim, @@ -756,6 +752,7 @@ impl<'a> TryFrom> for JournalEntry<'a> { st_atim, st_mtim, fst_flags, + _padding: _, }, ) => Self::FileDescriptorSetTimesV1 { fd: *fd, @@ -764,13 +761,21 @@ impl<'a> TryFrom> for JournalEntry<'a> { fst_flags: wasi::Fstflags::from_bits_truncate(*fst_flags), }, ArchivedJournalEntry::FileDescriptorSetSizeV1( - ArchivedJournalEntryFileDescriptorSetSizeV1 { fd, st_size }, + ArchivedJournalEntryFileDescriptorSetSizeV1 { + fd, + st_size, + _padding: _, + }, ) => Self::FileDescriptorSetSizeV1 { fd: *fd, st_size: *st_size, }, ArchivedJournalEntry::FileDescriptorSetFlagsV1( - ArchivedJournalEntryFileDescriptorSetFlagsV1 { fd, flags }, + ArchivedJournalEntryFileDescriptorSetFlagsV1 { + fd, + flags, + _padding: _, + }, ) => Self::FileDescriptorSetFlagsV1 { fd: *fd, flags: wasi::Fdflags::from_bits_truncate(*flags), @@ -800,7 +805,12 @@ impl<'a> TryFrom> for JournalEntry<'a> { advice: advice.into(), }, ArchivedJournalEntry::FileDescriptorAllocateV1( - ArchivedJournalEntryFileDescriptorAllocateV1 { fd, offset, len }, + ArchivedJournalEntryFileDescriptorAllocateV1 { + fd, + offset, + len, + _padding: _, + }, ) => Self::FileDescriptorAllocateV1 { fd: *fd, offset: *offset, @@ -815,10 +825,10 @@ impl<'a> TryFrom> for JournalEntry<'a> { _padding: _, }) => Self::CreateHardLinkV1 { old_fd: *old_fd, - old_path: old_path.as_ref().into(), + old_path: String::from_utf8_lossy(old_path.as_ref()), old_flags: *old_flags, new_fd: *new_fd, - new_path: new_path.as_ref().into(), + new_path: String::from_utf8_lossy(new_path.as_ref()), }, ArchivedJournalEntry::CreateSymbolicLinkV1( ArchivedJournalEntryCreateSymbolicLinkV1 { @@ -828,14 +838,14 @@ impl<'a> TryFrom> for JournalEntry<'a> { _padding: _, }, ) => Self::CreateSymbolicLinkV1 { - old_path: old_path.as_ref().into(), + old_path: String::from_utf8_lossy(old_path.as_ref()), fd: *fd, - new_path: new_path.as_ref().into(), + new_path: String::from_utf8_lossy(new_path.as_ref()), }, ArchivedJournalEntry::ChangeDirectoryV1(ArchivedJournalEntryChangeDirectoryV1 { path, }) => Self::ChangeDirectoryV1 { - path: path.as_ref().into(), + path: String::from_utf8_lossy(path.as_ref()), }, ArchivedJournalEntry::EpollCreateV1(ArchivedJournalEntryEpollCreateV1 { fd, @@ -902,10 +912,9 @@ impl<'a> TryFrom> for JournalEntry<'a> { network, token, ref security, - _padding: _, }) => Self::PortBridgeV1 { - network: network.as_ref().into(), - token: token.as_ref().into(), + network: String::from_utf8_lossy(network.as_ref()), + token: String::from_utf8_lossy(token.as_ref()), security: security.into(), }, ArchivedJournalEntry::PortUnbridgeV1 => Self::PortUnbridgeV1, @@ -955,7 +964,7 @@ impl<'a> TryFrom> for JournalEntry<'a> { ArchivedJournalEntry::SocketBindV1(ArchivedJournalEntrySocketBindV1 { fd, addr, - _padding, + _padding: _, }) => Self::SocketBindV1 { fd: *fd, addr: addr.as_socket_addr(), @@ -963,6 +972,7 @@ impl<'a> TryFrom> for JournalEntry<'a> { ArchivedJournalEntry::SocketConnectedV1(ArchivedJournalEntrySocketConnectedV1 { fd, addr, + _padding: _, }) => Self::SocketConnectedV1 { fd: *fd, addr: addr.as_socket_addr(), @@ -1041,7 +1051,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { flags, addr, is_64bit, - _padding: _, }) => Self::SocketSendToV1 { fd: *fd, data: data.as_ref().into(), @@ -1054,7 +1063,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { data, flags, is_64bit, - _padding: _, }) => Self::SocketSendV1 { fd: *fd, data: data.as_ref().into(), @@ -1099,6 +1107,7 @@ impl<'a> TryFrom> for JournalEntry<'a> { initial_val, flags, fd, + _padding: _, }) => Self::CreateEventV1 { initial_val: *initial_val, flags: *flags, diff --git a/lib/journal/src/concrete/log_file.rs b/lib/journal/src/concrete/log_file.rs index a254b7e7e11..ca39f93717e 100644 --- a/lib/journal/src/concrete/log_file.rs +++ b/lib/journal/src/concrete/log_file.rs @@ -1,6 +1,7 @@ use bytes::Buf; -use rkyv::ser::serializers::{ - AllocScratch, CompositeSerializer, SharedSerializeMap, WriteSerializer, +use rkyv::ser::{ + serializers::{AllocScratch, CompositeSerializer, SharedSerializeMap, WriteSerializer}, + Serializer, }; use shared_buffer::OwnedBuffer; use std::{ @@ -23,6 +24,7 @@ use super::*; /// /// The logfile snapshot capturer uses a 64bit number as a entry encoding /// delimiter. +#[derive(Debug)] pub struct LogFileJournal { tx: LogFileJournalTx, rx: LogFileJournalRx, @@ -89,21 +91,23 @@ impl LogFileJournal { Self::from_file(file) } - pub fn from_file(mut file: std::fs::File) -> anyhow::Result { + pub fn from_file(file: std::fs::File) -> anyhow::Result { // Move to the end of the file and write the // magic if one is needed - if file.seek(SeekFrom::End(0)).unwrap() == 0 { + let underlying_file = file.try_clone()?; + let mut serializer = WriteSerializer::new(file); + if serializer.pos() == 0 { let magic = JOURNAL_MAGIC_NUMBER; let magic = magic.to_be_bytes(); - file.write_all(&magic)?; + serializer.write(&magic)?; } // Create the tx let tx = LogFileJournalTx { state: Arc::new(Mutex::new(TxState { - file: file.try_clone()?, + file: underlying_file, serializer: CompositeSerializer::new( - WriteSerializer::new(file), + serializer, AllocScratch::default(), SharedSerializeMap::default(), ), @@ -125,30 +129,34 @@ impl WritableJournal for LogFileJournalTx { // Write the header (with a record size of zero) let record_type: JournalEntryRecordType = entry.archive_record_type(); - state.file.write_all(&(record_type as u16).to_be_bytes())?; - let offset_size = state.file.stream_position()?; - state.file.write_all(&[0u8; 6])?; // record and pad size (48 bits) - - // Now serialize the actual data to the log - let offset_start = state.file.stream_position()?; + let offset_header = state.file.stream_position()?; + state.serializer.write(&[0u8; 8])?; + + // Now serialize the actual data to the log (we need to pad to a + // location that is aligned with the structure) + let offset_start = state.serializer.pos() as u64; + if state.serializer.pos() as u64 != offset_start { + return Err(anyhow::format_err!( + "serializer has misaligned positioning ({} vs {offset_start})", + state.serializer.pos() + )); + } entry.serialize_archive(&mut state.serializer)?; - let offset_end = state.file.stream_position()?; + let offset_end = state.serializer.pos() as u64; let record_size = offset_end - offset_start; - - // If the alightment is out then fail - if record_size % 8 != 0 { - tracing::error!( - "alignment is out for journal event (type={:?}, record_size={}, alignment={})", - record_type, - record_size, - record_size % 8 - ); - } + tracing::trace!( + "delimiter header={offset_header},start={offset_start},record_size={record_size}" + ); // Write the record and then move back to the end again - state.file.seek(SeekFrom::Start(offset_size))?; - state.file.write_all(&record_size.to_be_bytes()[2..8])?; - state.file.seek(SeekFrom::Start(offset_end))?; + state.file.seek(SeekFrom::Start(offset_header))?; + let header_bytes = { + let a = (record_type as u16).to_be_bytes(); + let b = &record_size.to_be_bytes()[2..8]; + [a[0], a[1], b[0], b[1], b[2], b[3], b[4], b[5]] + }; + state.file.write_all(&header_bytes)?; + state.file.seek(SeekFrom::End(0))?; // Now write the actual data and update the offsets Ok(record_size) @@ -194,16 +202,6 @@ impl ReadableJournal for LogFileJournalRx { header }; - if header.record_size as usize > buffer_ptr.len() { - *buffer_pos += buffer_ptr.len(); - tracing::trace!( - "journal is corrupt (record_size={} vs remaining={})", - header.record_size, - buffer_ptr.len() - ); - return Ok(None); - } - // Move the buffer position forward past the record let entry = &buffer_ptr[..(header.record_size as usize)]; buffer_ptr.advance(header.record_size as usize); diff --git a/lib/journal/src/concrete/printing.rs b/lib/journal/src/concrete/printing.rs index c12ea6e2fac..0987c07ddd5 100644 --- a/lib/journal/src/concrete/printing.rs +++ b/lib/journal/src/concrete/printing.rs @@ -136,11 +136,11 @@ impl<'a> fmt::Display for JournalEntry<'a> { "fd-duplicate (original={}, copied={})", original_fd, copied_fd ), - JournalEntry::CreateDirectoryV1 { path, .. } => { - write!(f, "path-create-dir (path={})", path) + JournalEntry::CreateDirectoryV1 { fd, path } => { + write!(f, "path-create-dir (fd={}, path={})", fd, path) } - JournalEntry::RemoveDirectoryV1 { path, .. } => { - write!(f, "path-remove-dir (path={})", path) + JournalEntry::RemoveDirectoryV1 { fd, path } => { + write!(f, "path-remove-dir (fd={}, path={})", fd, path) } JournalEntry::PathSetTimesV1 { path, diff --git a/lib/virtual-fs/src/mem_fs/offloaded_file.rs b/lib/virtual-fs/src/mem_fs/offloaded_file.rs index 20cf8a69887..d4731589ff7 100644 --- a/lib/virtual-fs/src/mem_fs/offloaded_file.rs +++ b/lib/virtual-fs/src/mem_fs/offloaded_file.rs @@ -8,7 +8,7 @@ use crate::limiter::DynFsMemoryLimiter; pub enum FileExtent { MmapOffload { offset: u64, size: u64 }, RepeatingBytes { value: u8, cnt: u64 }, - Bytes { data: Bytes }, + InMemory { data: Bytes }, } impl FileExtent { @@ -16,7 +16,7 @@ impl FileExtent { match self { FileExtent::MmapOffload { size, .. } => *size, FileExtent::RepeatingBytes { cnt, .. } => *cnt, - FileExtent::Bytes { data } => data.len() as u64, + FileExtent::InMemory { data } => data.len() as u64, } } @@ -24,7 +24,7 @@ impl FileExtent { match self { FileExtent::MmapOffload { size, .. } => *size = new_size.min(*size), FileExtent::RepeatingBytes { cnt, .. } => *cnt = new_size, - FileExtent::Bytes { data } => { + FileExtent::InMemory { data } => { *data = data.slice(..(new_size as usize)); } } @@ -108,7 +108,7 @@ impl OffloadedFile { }); cnt } - FileExtent::Bytes { data } => { + FileExtent::InMemory { data } => { let data = &data.as_ref()[extent_offset as usize..]; let data_len = cmp::min(buf.len(), data.len()); buf[..data_len].copy_from_slice(&data[..data_len]); @@ -156,7 +156,7 @@ impl OffloadedFile { value: *other_value, cnt: *other_cnt - split_at, }, - FileExtent::Bytes { data: other_data } => FileExtent::Bytes { + FileExtent::InMemory { data: other_data } => FileExtent::InMemory { data: other_data.slice((split_at as usize)..), }, }; @@ -205,7 +205,7 @@ impl OffloadedFile { size: data_end - data_start, } } else { - FileExtent::Bytes { + FileExtent::InMemory { data: data.to_vec().into(), } }; diff --git a/lib/wasix/src/fs/mod.rs b/lib/wasix/src/fs/mod.rs index 965377b3cff..b97b885eda7 100644 --- a/lib/wasix/src/fs/mod.rs +++ b/lib/wasix/src/fs/mod.rs @@ -410,6 +410,60 @@ fn create_dir_all(fs: &dyn FileSystem, path: &Path) -> Result<(), virtual_fs::Fs Ok(()) } +#[derive(Debug, Clone)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +pub struct WasiFdSeed { + next_fd: Arc, +} + +impl Default for WasiFdSeed { + fn default() -> Self { + Self::new(3) + } +} + +impl WasiFdSeed { + pub fn new(initial_val: u32) -> Self { + Self { + next_fd: Arc::new(AtomicU32::new(initial_val)), + } + } + + pub fn fork(&self) -> Self { + Self { + next_fd: Arc::new(AtomicU32::new(self.next_fd.load(Ordering::SeqCst))), + } + } + + pub fn next_val(&self) -> WasiFd { + self.next_fd.fetch_add(1, Ordering::SeqCst) + } + + pub fn set_val(&self, val: WasiFd) { + self.next_fd.store(val, std::sync::atomic::Ordering::SeqCst) + } + + pub fn cur_val(&self) -> WasiFd { + self.next_fd.load(Ordering::SeqCst) + } + + pub fn clip_val(&self, fd: WasiFd) { + loop { + let existing = self.next_fd.load(Ordering::SeqCst); + if existing >= fd { + return; + } + if self + .next_fd + .compare_exchange(existing, fd, Ordering::SeqCst, Ordering::Relaxed) + .is_ok() + { + break; + } + } + } +} + /// Warning, modifying these fields directly may cause invariants to break and /// should be considered unsafe. These fields may be made private in a future release #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] @@ -417,7 +471,7 @@ pub struct WasiFs { //pub repo: Repo, pub preopen_fds: RwLock>, pub fd_map: Arc>>, - pub next_fd: AtomicU32, + pub next_fd: WasiFdSeed, pub current_dir: Mutex, #[cfg_attr(feature = "enable-serde", serde(skip, default))] pub root_fs: WasiFsRoot, @@ -453,7 +507,7 @@ impl WasiFs { Self { preopen_fds: RwLock::new(self.preopen_fds.read().unwrap().clone()), fd_map: Arc::new(RwLock::new(fd_map)), - next_fd: AtomicU32::new(self.next_fd.load(Ordering::SeqCst)), + next_fd: self.next_fd.fork(), current_dir: Mutex::new(self.current_dir.lock().unwrap().clone()), is_wasix: AtomicBool::new(self.is_wasix.load(Ordering::Acquire)), root_fs: self.root_fs.clone(), @@ -563,7 +617,7 @@ impl WasiFs { let wasi_fs = Self { preopen_fds: RwLock::new(vec![]), fd_map: Arc::new(RwLock::new(HashMap::new())), - next_fd: AtomicU32::new(3), + next_fd: WasiFdSeed::default(), current_dir: Mutex::new("/".to_string()), is_wasix: AtomicBool::new(false), root_fs: fs_backing, @@ -689,7 +743,7 @@ impl WasiFs { let kind = Kind::File { handle: Some(Arc::new(RwLock::new(file))), path: PathBuf::from(""), - fd: Some(self.next_fd.fetch_add(1, Ordering::SeqCst)), + fd: Some(self.next_fd.next_val()), }; drop(guard); @@ -1250,6 +1304,9 @@ impl WasiFs { } pub fn get_fd_inode(&self, fd: WasiFd) -> Result { + if fd == VIRTUAL_ROOT_FD { + return Ok(self.root_inode.clone()); + } self.fd_map .read() .unwrap() @@ -1471,25 +1528,13 @@ impl WasiFs { open_flags: u16, inode: InodeGuard, ) -> Result { - let idx = self.next_fd.fetch_add(1, Ordering::SeqCst); + let idx = self.next_fd.next_val(); self.create_fd_ext(rights, rights_inheriting, flags, open_flags, inode, idx)?; Ok(idx) } pub fn make_max_fd(&self, fd: u32) { - loop { - let existing = self.next_fd.load(Ordering::SeqCst); - if existing >= fd { - return; - } - if self - .next_fd - .compare_exchange(existing, fd, Ordering::SeqCst, Ordering::Relaxed) - .is_ok() - { - break; - } - } + self.next_fd.clip_val(fd); } pub fn create_fd_ext( @@ -1522,7 +1567,7 @@ impl WasiFs { pub fn clone_fd(&self, fd: WasiFd) -> Result { let fd = self.get_fd(fd)?; - let idx = self.next_fd.fetch_add(1, Ordering::SeqCst); + let idx = self.next_fd.next_val(); self.fd_map.write().unwrap().insert( idx, Fd { @@ -1925,7 +1970,7 @@ impl std::fmt::Debug for WasiFs { } else { write!(f, "current_dir=(locked) ")?; } - write!(f, "next_fd={} ", self.next_fd.load(Ordering::Relaxed))?; + write!(f, "next_fd={} ", self.next_fd.cur_val())?; write!(f, "{:?}", self.root_fs) } } diff --git a/lib/wasix/src/journal/concrete/archived.rs b/lib/wasix/src/journal/concrete/archived.rs index 00f67bbdc3f..9d3a9317ad5 100644 --- a/lib/wasix/src/journal/concrete/archived.rs +++ b/lib/wasix/src/journal/concrete/archived.rs @@ -448,8 +448,7 @@ impl<'a> JournalEntry<'a> { serializer.serialize_value(&JournalEntryUpdateMemoryRegionV1 { start: region.start, end: region.end, - _padding: padding(data.len()), - compressed_data: compress_prepend_size(data.as_ref()), + compressed_data: to_aligned_vec(compress_prepend_size(data.as_ref())), }) } JournalEntry::ProcessExitV1 { exit_code } => { @@ -466,10 +465,9 @@ impl<'a> JournalEntry<'a> { is_64bit, } => serializer.serialize_value(&JournalEntrySetThreadV1 { id: id.into(), - _padding: padding(call_stack.len() + memory_stack.len() + store_data.len()), - call_stack: call_stack.into_owned(), - memory_stack: memory_stack.into_owned(), - store_data: store_data.into_owned(), + call_stack: to_aligned_vec(call_stack), + memory_stack: to_aligned_vec_str(memory_stack), + store_data: to_aligned_vec_str(store_data), is_64bit, }), JournalEntry::CloseThreadV1 { id, exit_code } => { @@ -492,8 +490,7 @@ impl<'a> JournalEntry<'a> { } => serializer.serialize_value(&JournalEntryFileDescriptorWriteV1 { fd, offset, - _padding: padding(data.len()), - data: data.into_owned(), + data: to_aligned_vec(data), is_64bit, }), JournalEntry::SetClockTimeV1 { clock_id, time } => { @@ -518,8 +515,7 @@ impl<'a> JournalEntry<'a> { fd, dirfd, dirflags, - _padding: padding(path.as_bytes().len()), - path: path.into_owned(), + path: to_aligned_vec_str(path), o_flags: o_flags.bits(), fs_rights_base: fs_rights_base.bits(), fs_rights_inheriting: fs_rights_inheriting.bits(), @@ -538,15 +534,13 @@ impl<'a> JournalEntry<'a> { JournalEntry::CreateDirectoryV1 { fd, path } => { serializer.serialize_value(&JournalEntryCreateDirectoryV1 { fd, - _padding: padding(path.as_bytes().len()), - path: path.into_owned(), + path: to_aligned_vec_str(path), }) } JournalEntry::RemoveDirectoryV1 { fd, path } => { serializer.serialize_value(&JournalEntryRemoveDirectoryV1 { fd, - _padding: padding(path.as_bytes().len()), - path: path.into_owned(), + path: to_aligned_vec_str(path), }) } JournalEntry::PathSetTimesV1 { @@ -559,8 +553,7 @@ impl<'a> JournalEntry<'a> { } => serializer.serialize_value(&JournalEntryPathSetTimesV1 { fd, flags, - _padding: padding(path.as_bytes().len()), - path: path.into_owned(), + path: to_aligned_vec_str(path)), st_atim, st_mtim, fst_flags: fst_flags.bits(), @@ -615,27 +608,24 @@ impl<'a> JournalEntry<'a> { new_path, } => serializer.serialize_value(&JournalEntryCreateHardLinkV1 { old_fd, - _padding: padding(old_path.as_bytes().len() + new_path.as_bytes().len()), - old_path: old_path.into_owned(), + old_path: to_aligned_vec_str(old_path), old_flags, new_fd, - new_path: new_path.into_owned(), + new_path: to_aligned_vec_str(new_path), }), JournalEntry::CreateSymbolicLinkV1 { old_path, fd, new_path, } => serializer.serialize_value(&JournalEntryCreateSymbolicLinkV1 { - _padding: padding(old_path.as_bytes().len() + new_path.as_bytes().len()), - old_path: old_path.into_owned(), + old_path: to_aligned_vec_str(old_path), fd, - new_path: new_path.into_owned(), + new_path: to_aligned_vec_str(new_path), }), JournalEntry::UnlinkFileV1 { fd, path } => { serializer.serialize_value(&JournalEntryUnlinkFileV1 { fd, - _padding: padding(path.as_bytes().len()), - path: path.into_owned(), + path: to_aligned_vec_str(path), }) } JournalEntry::PathRenameV1 { @@ -645,14 +635,13 @@ impl<'a> JournalEntry<'a> { new_path, } => serializer.serialize_value(&JournalEntryPathRenameV1 { old_fd, - _padding: padding(old_path.as_bytes().len() + new_path.as_bytes().len()), - old_path: old_path.into_owned(), + old_path: to_aligned_vec_str(old_path), new_fd, - new_path: new_path.into_owned(), + new_path: to_aligned_vec_str(new_path), }), JournalEntry::ChangeDirectoryV1 { path } => { serializer.serialize_value(&JournalEntryChangeDirectoryV1 { - path: path.into_owned(), + path: to_aligned_vec_str(path), }) } JournalEntry::EpollCreateV1 { fd } => { @@ -707,9 +696,8 @@ impl<'a> JournalEntry<'a> { token, security, } => serializer.serialize_value(&JournalEntryPortBridgeV1 { - _padding: padding(network.as_bytes().len() + token.as_bytes().len()), - network: network.into_owned(), - token: token.into_owned(), + network: to_aligned_vec_str(network), + token: to_aligned_vec_str(token), security: security.into(), }), JournalEntry::PortUnbridgeV1 => return Ok(()), @@ -830,8 +818,7 @@ impl<'a> JournalEntry<'a> { is_64bit, } => serializer.serialize_value(&JournalEntrySocketSendV1 { fd, - _padding: padding(data.len()), - data: data.into_owned(), + data: to_aligned_vec(data), flags, is_64bit, }), @@ -2326,7 +2313,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { start, end, compressed_data, - _padding: _, }, ) => Self::UpdateMemoryRegionV1 { region: (*start)..(*end), @@ -2334,7 +2320,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { }, ArchivedJournalEntry::ProcessExitV1(ArchivedJournalEntryProcessExitV1 { exit_code, - _padding: _, }) => Self::ProcessExitV1 { exit_code: exit_code.as_ref().map(|code| code.into()), }, @@ -2343,7 +2328,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { call_stack, memory_stack, store_data, - _padding: _, is_64bit, }) => Self::SetThreadV1 { id: (*id).into(), @@ -2365,7 +2349,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { fd, offset, is_64bit, - _padding: _, }, ) => Self::FileDescriptorWriteV1 { data: data.as_ref().into(), @@ -2394,7 +2377,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { fs_rights_base, fs_rights_inheriting, fs_flags, - _padding: _, }, ) => Self::OpenFileDescriptorV1 { fd: *fd, @@ -2407,30 +2389,26 @@ impl<'a> TryFrom> for JournalEntry<'a> { fs_flags: wasi::Fdflags::from_bits_truncate(*fs_flags), }, ArchivedJournalEntry::CloseFileDescriptorV1( - ArchivedJournalEntryCloseFileDescriptorV1 { fd, _padding: _ }, + ArchivedJournalEntryCloseFileDescriptorV1 { fd }, ) => Self::CloseFileDescriptorV1 { fd: *fd }, ArchivedJournalEntry::RemoveDirectoryV1(ArchivedJournalEntryRemoveDirectoryV1 { fd, path, - _padding: _, }) => Self::RemoveDirectoryV1 { fd: *fd, path: path.as_ref().into(), }, - ArchivedJournalEntry::UnlinkFileV1(ArchivedJournalEntryUnlinkFileV1 { - fd, - path, - _padding: _, - }) => Self::UnlinkFileV1 { - fd: *fd, - path: path.as_ref().into(), - }, + ArchivedJournalEntry::UnlinkFileV1(ArchivedJournalEntryUnlinkFileV1 { fd, path }) => { + Self::UnlinkFileV1 { + fd: *fd, + path: path.as_ref().into(), + } + } ArchivedJournalEntry::PathRenameV1(ArchivedJournalEntryPathRenameV1 { old_fd, old_path, new_fd, new_path, - _padding: _, }) => Self::PathRenameV1 { old_fd: *old_fd, old_path: old_path.as_ref().into(), @@ -2471,7 +2449,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { ArchivedJournalEntry::CreateDirectoryV1(ArchivedJournalEntryCreateDirectoryV1 { fd, path, - _padding: _, }) => Self::CreateDirectoryV1 { fd: *fd, path: path.as_ref().into(), @@ -2483,7 +2460,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { st_atim, st_mtim, fst_flags, - _padding: _, }) => Self::PathSetTimesV1 { fd: *fd, path: path.as_ref().into(), @@ -2554,7 +2530,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { old_flags, new_fd, new_path, - _padding: _, }) => Self::CreateHardLinkV1 { old_fd: *old_fd, old_path: old_path.as_ref().into(), @@ -2567,7 +2542,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { old_path, fd, new_path, - _padding: _, }, ) => Self::CreateSymbolicLinkV1 { old_path: old_path.as_ref().into(), @@ -2579,10 +2553,9 @@ impl<'a> TryFrom> for JournalEntry<'a> { }) => Self::ChangeDirectoryV1 { path: path.as_ref().into(), }, - ArchivedJournalEntry::EpollCreateV1(ArchivedJournalEntryEpollCreateV1 { - fd, - _padding: _, - }) => Self::EpollCreateV1 { fd: *fd }, + ArchivedJournalEntry::EpollCreateV1(ArchivedJournalEntryEpollCreateV1 { fd }) => { + Self::EpollCreateV1 { fd: *fd } + } ArchivedJournalEntry::EpollCtlV1(ArchivedJournalEntryEpollCtlV1 { epfd, ref op, @@ -2643,7 +2616,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { network, token, ref security, - _padding: _, }) => Self::PortBridgeV1 { network: network.as_ref().into(), token: token.as_ref().into(), @@ -2779,7 +2751,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { flags, addr, is_64bit, - _padding: _, }) => Self::SocketSendToV1 { fd: *fd, data: data.as_ref().into(), @@ -2792,7 +2763,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { data, flags, is_64bit, - _padding: _, }) => Self::SocketSendV1 { fd: *fd, data: data.as_ref().into(), diff --git a/lib/wasix/src/state/env.rs b/lib/wasix/src/state/env.rs index 71c331dbf43..3ba1e0d4d10 100644 --- a/lib/wasix/src/state/env.rs +++ b/lib/wasix/src/state/env.rs @@ -425,10 +425,7 @@ impl WasiEnv { map.clear(); } self.state.fs.preopen_fds.write().unwrap().clear(); - self.state - .fs - .next_fd - .store(3, std::sync::atomic::Ordering::SeqCst); + self.state.fs.next_fd.set_val(3); *self.state.fs.current_dir.lock().unwrap() = "/".to_string(); // We need to rebuild the basic file descriptors From 1fab2ad7ea020dc5e1647c1382886737c44ccad6 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Tue, 23 Jan 2024 03:26:53 +1100 Subject: [PATCH 13/67] Fixed an issue with a lock on the file system --- lib/cli/src/commands/journal/mount/cmd.rs | 28 +++++++++++++---------- lib/cli/src/commands/journal/mount/fs.rs | 2 ++ lib/virtual-fs/src/mem_fs/file_opener.rs | 6 +---- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/lib/cli/src/commands/journal/mount/cmd.rs b/lib/cli/src/commands/journal/mount/cmd.rs index 0a79bbfd419..f19e4ebf322 100644 --- a/lib/cli/src/commands/journal/mount/cmd.rs +++ b/lib/cli/src/commands/journal/mount/cmd.rs @@ -25,18 +25,22 @@ impl AsyncCliCommand for CmdJournalMount { impl CmdJournalMount { async fn run(self) -> Result<(), anyhow::Error> { - // First we unmount any existing file system on this path - std::process::Command::new("/bin/umount") - .arg(self.mount_path.to_string_lossy().as_ref()) - .stderr(Stdio::null()) - .stdout(Stdio::null()) - .spawn()? - .wait() - .ok(); + let fs: JournalFileSystem = + JournalFileSystem::new(&self.journal_path, WasiFdSeed::default())?; + tokio::task::spawn_blocking(move || { + // First we unmount any existing file system on this path + std::process::Command::new("/bin/umount") + .arg(self.mount_path.to_string_lossy().as_ref()) + .stderr(Stdio::null()) + .stdout(Stdio::null()) + .spawn()? + .wait() + .ok(); - // Mounts the journal file system at a path - let fs = JournalFileSystem::new(&self.journal_path, WasiFdSeed::default())?; - fuse::mount(fs, &self.mount_path, &[])?; - Ok(()) + // Mounts the journal file system at a path + fuse::mount(fs, &self.mount_path, &[])?; + Ok(()) + }) + .await? } } diff --git a/lib/cli/src/commands/journal/mount/fs.rs b/lib/cli/src/commands/journal/mount/fs.rs index ab9c8e068da..d37b5728784 100644 --- a/lib/cli/src/commands/journal/mount/fs.rs +++ b/lib/cli/src/commands/journal/mount/fs.rs @@ -327,6 +327,7 @@ impl Filesystem for JournalFileSystem { state .journal_lookup .insert(fh, Arc::new(tokio::sync::Mutex::new(file))); + drop(state); // Write the journals let entry = JournalEntry::OpenFileDescriptorV1 { @@ -392,6 +393,7 @@ impl Filesystem for JournalFileSystem { state .journal_lookup .insert(fh, Arc::new(tokio::sync::Mutex::new(file))); + drop(state); // Write the journals let entry = JournalEntry::OpenFileDescriptorV1 { diff --git a/lib/virtual-fs/src/mem_fs/file_opener.rs b/lib/virtual-fs/src/mem_fs/file_opener.rs index 2e69aa42678..a45bd562749 100644 --- a/lib/virtual-fs/src/mem_fs/file_opener.rs +++ b/lib/virtual-fs/src/mem_fs/file_opener.rs @@ -502,11 +502,7 @@ impl crate::FileOpener for FileSystem { let inode_of_file = fs.storage.vacant_entry().key(); // We might be in optimized mode - let offload = { - let inner = self.inner.read().map_err(|_| FsError::Lock)?; - inner.mmap_offload.clone() - }; - let file = if let Some(offload) = offload { + let file = if let Some(offload) = fs.mmap_offload.clone() { let file = OffloadedFile::new(fs.limiter.clone(), offload); Node::OffloadedFile(OffloadedFileNode { inode: inode_of_file, From 88c984de48a649593c0ed90075c0c0f9c1fdfdb0 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Tue, 23 Jan 2024 03:57:04 +1100 Subject: [PATCH 14/67] Fixes for the journal fd seed --- lib/cli/src/commands/journal/mount/fs.rs | 24 ++++++++++++++++++++++ lib/journal/src/concrete/log_file.rs | 5 +++-- lib/wasix/src/journal/concrete/archived.rs | 2 +- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/lib/cli/src/commands/journal/mount/fs.rs b/lib/cli/src/commands/journal/mount/fs.rs index d37b5728784..392f0e85501 100644 --- a/lib/cli/src/commands/journal/mount/fs.rs +++ b/lib/cli/src/commands/journal/mount/fs.rs @@ -234,6 +234,30 @@ impl WritableJournal for MutexState { .await })?; } + JournalEntry::SocketOpenV1 { fd, .. } => { + state.seed.clip_val(fd + 1); + } + JournalEntry::CreatePipeV1 { fd1, fd2 } => { + state.seed.clip_val(fd1 + 1); + state.seed.clip_val(fd2 + 1); + } + JournalEntry::CreateEventV1 { fd, .. } => { + state.seed.clip_val(fd + 1); + } + JournalEntry::EpollCreateV1 { fd } => { + state.seed.clip_val(fd + 1); + } + JournalEntry::EpollCtlV1 { + epfd, + op, + fd, + event, + } => { + state.seed.clip_val(fd + 1); + } + JournalEntry::SocketAcceptedV1 { fd, .. } => { + state.seed.clip_val(fd + 1); + } _ => {} } Ok(ret) diff --git a/lib/journal/src/concrete/log_file.rs b/lib/journal/src/concrete/log_file.rs index ca39f93717e..cc04b59746d 100644 --- a/lib/journal/src/concrete/log_file.rs +++ b/lib/journal/src/concrete/log_file.rs @@ -91,11 +91,12 @@ impl LogFileJournal { Self::from_file(file) } - pub fn from_file(file: std::fs::File) -> anyhow::Result { + pub fn from_file(mut file: std::fs::File) -> anyhow::Result { // Move to the end of the file and write the // magic if one is needed let underlying_file = file.try_clone()?; - let mut serializer = WriteSerializer::new(file); + let end_pos = file.seek(SeekFrom::End(0))?; + let mut serializer = WriteSerializer::with_pos(file, end_pos as usize); if serializer.pos() == 0 { let magic = JOURNAL_MAGIC_NUMBER; let magic = magic.to_be_bytes(); diff --git a/lib/wasix/src/journal/concrete/archived.rs b/lib/wasix/src/journal/concrete/archived.rs index 9d3a9317ad5..9546cee699e 100644 --- a/lib/wasix/src/journal/concrete/archived.rs +++ b/lib/wasix/src/journal/concrete/archived.rs @@ -553,7 +553,7 @@ impl<'a> JournalEntry<'a> { } => serializer.serialize_value(&JournalEntryPathSetTimesV1 { fd, flags, - path: to_aligned_vec_str(path)), + path: to_aligned_vec_str(path), st_atim, st_mtim, fst_flags: fst_flags.bits(), From 41528eb1cab433f643b284a93e6bd5934dbccdf4 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Tue, 23 Jan 2024 04:28:24 +1100 Subject: [PATCH 15/67] Fixed some compile issues --- lib/cli/src/commands/journal/mount/fs.rs | 2 +- lib/journal/src/concrete/archived.rs | 6 ++++-- lib/journal/src/concrete/archived_from.rs | 3 ++- lib/virtual-net/Cargo.toml | 6 +++--- lib/virtual-net/src/loopback.rs | 5 +++-- lib/virtual-net/src/tcp_pair.rs | 6 +++--- 6 files changed, 16 insertions(+), 12 deletions(-) diff --git a/lib/cli/src/commands/journal/mount/fs.rs b/lib/cli/src/commands/journal/mount/fs.rs index 392f0e85501..8950c65cf3c 100644 --- a/lib/cli/src/commands/journal/mount/fs.rs +++ b/lib/cli/src/commands/journal/mount/fs.rs @@ -425,7 +425,7 @@ impl Filesystem for JournalFileSystem { dirfd: VIRTUAL_ROOT_FD, dirflags: 0, path, - o_flags: wasi::Oflags::empty(), + o_flags: wasi::Oflags::CREATE, fs_rights_base: wasi::Rights::all(), fs_rights_inheriting: wasi::Rights::all(), fs_flags: wasi::Fdflags::empty(), diff --git a/lib/journal/src/concrete/archived.rs b/lib/journal/src/concrete/archived.rs index 9a3af4ee87e..6fb0e0d137c 100644 --- a/lib/journal/src/concrete/archived.rs +++ b/lib/journal/src/concrete/archived.rs @@ -525,7 +525,8 @@ impl<'a> JournalEntry<'a> { serializer.serialize_value(&JournalEntryCreateDirectoryV1 { fd, path: to_aligned_vec(path.as_bytes()), - _padding: 0, + _padding1: 0, + _padding2: 0, }) } JournalEntry::RemoveDirectoryV1 { fd, path } => { @@ -1098,7 +1099,8 @@ pub struct JournalEntryDuplicateFileDescriptorV1 { #[archive_attr(derive(CheckBytes))] pub struct JournalEntryCreateDirectoryV1 { pub fd: u32, - pub _padding: u32, + pub _padding1: u32, + pub _padding2: u64, pub path: AlignedVec, } diff --git a/lib/journal/src/concrete/archived_from.rs b/lib/journal/src/concrete/archived_from.rs index f5f12c5fffb..938fab72ff2 100644 --- a/lib/journal/src/concrete/archived_from.rs +++ b/lib/journal/src/concrete/archived_from.rs @@ -724,7 +724,8 @@ impl<'a> TryFrom> for JournalEntry<'a> { ArchivedJournalEntry::CreateDirectoryV1(ArchivedJournalEntryCreateDirectoryV1 { fd, path, - _padding: _, + _padding1: _, + _padding2: _, }) => Self::CreateDirectoryV1 { fd: *fd, path: String::from_utf8_lossy(path.as_ref()), diff --git a/lib/virtual-net/Cargo.toml b/lib/virtual-net/Cargo.toml index 654086be6ec..ed087b1ccdb 100644 --- a/lib/virtual-net/Cargo.toml +++ b/lib/virtual-net/Cargo.toml @@ -14,7 +14,7 @@ thiserror = "1" bytes = "1.1" async-trait = { version = "^0.1" } tracing = "0.1" -tokio = { version = "1", default_features = false, optional = true } +tokio = { version = "1", default_features = false, features = ["io-util"] } libc = { version = "0.2.139", optional = true } mio = { version = "0.8", optional = true } socket2 = { version = "0.4", optional = true } @@ -46,8 +46,8 @@ serial_test = "2.0.0" [features] default = [ "host-net", "remote", "json", "messagepack", "cbor", "hyper", "tokio-tungstenite" ] -host-net = [ "tokio", "libc", "tokio/io-util", "virtual-mio/sys", "tokio/net", "tokio/rt", "socket2", "mio" ] -remote = [ "tokio", "libc", "tokio/io-util", "tokio/sync", "tokio-serde", "tokio-util" ] +host-net = [ "libc", "tokio/io-util", "virtual-mio/sys", "tokio/net", "tokio/rt", "socket2", "mio" ] +remote = [ "libc", "tokio/io-util", "tokio/sync", "tokio-serde", "tokio-util" ] json = [ "tokio-serde/json" ] messagepack = [ "tokio-serde/messagepack" ] cbor = [ "tokio-serde/cbor" ] diff --git a/lib/virtual-net/src/loopback.rs b/lib/virtual-net/src/loopback.rs index cc0cfb1e05a..f6da120fd7e 100644 --- a/lib/virtual-net/src/loopback.rs +++ b/lib/virtual-net/src/loopback.rs @@ -6,10 +6,11 @@ use std::{collections::HashMap, sync::Arc}; use crate::tcp_pair::TcpSocketHalf; use crate::{ - InterestHandler, InterestType, IpAddr, IpCidr, Ipv4Addr, Ipv6Addr, NetworkError, - VirtualIoSource, VirtualNetworking, VirtualTcpListener, VirtualTcpSocket, + InterestHandler, IpAddr, IpCidr, Ipv4Addr, Ipv6Addr, NetworkError, VirtualIoSource, + VirtualNetworking, VirtualTcpListener, VirtualTcpSocket, }; use derivative::Derivative; +use virtual_mio::InterestType; const DEFAULT_MAX_BUFFER_SIZE: usize = 1_048_576; diff --git a/lib/virtual-net/src/tcp_pair.rs b/lib/virtual-net/src/tcp_pair.rs index f60652d293e..54528000746 100644 --- a/lib/virtual-net/src/tcp_pair.rs +++ b/lib/virtual-net/src/tcp_pair.rs @@ -1,6 +1,6 @@ use crate::{ - net_error_into_io_err, InterestHandler, InterestType, NetworkError, SocketStatus, - VirtualConnectedSocket, VirtualIoSource, VirtualSocket, VirtualTcpSocket, + net_error_into_io_err, InterestHandler, NetworkError, SocketStatus, VirtualConnectedSocket, + VirtualIoSource, VirtualSocket, VirtualTcpSocket, }; use bytes::{Buf, Bytes}; use futures_util::Future; @@ -13,7 +13,7 @@ use std::task::{Context, Waker}; use std::time::Duration; use std::{net::SocketAddr, task::Poll}; use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite, BufReader}; -use virtual_mio::ArcInterestHandler; +use virtual_mio::{ArcInterestHandler, InterestType}; #[derive(Debug)] struct SocketBufferState { From 020fcac08cf7140c3222ad723b1061677fae819b Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Tue, 23 Jan 2024 04:45:34 +1100 Subject: [PATCH 16/67] Finally fixed the journal alignment issues, once and for all --- lib/journal/src/concrete/archived.rs | 957 +++++++++++----------- lib/journal/src/concrete/archived_from.rs | 67 +- 2 files changed, 475 insertions(+), 549 deletions(-) diff --git a/lib/journal/src/concrete/archived.rs b/lib/journal/src/concrete/archived.rs index 6fb0e0d137c..95f325b3c2f 100644 --- a/lib/journal/src/concrete/archived.rs +++ b/lib/journal/src/concrete/archived.rs @@ -431,450 +431,423 @@ impl<'a> JournalEntry<'a> { where T::Error: std::fmt::Display, { - let amt = - match self { - JournalEntry::InitModuleV1 { wasm_hash } => { - serializer.serialize_value(&JournalEntryInitModuleV1 { wasm_hash }) - } - JournalEntry::UpdateMemoryRegionV1 { region, data } => { - serializer.serialize_value(&JournalEntryUpdateMemoryRegionV1 { - start: region.start, - end: region.end, - compressed_data: to_aligned_vec(compress_prepend_size(data.as_ref())), - }) - } - JournalEntry::ProcessExitV1 { exit_code } => { - serializer.serialize_value(&JournalEntryProcessExitV1 { - exit_code: exit_code.map(|e| e.into()), - _padding: 0, - }) - } - JournalEntry::SetThreadV1 { - id, - call_stack, - memory_stack, - store_data, - is_64bit, - } => serializer.serialize_value(&JournalEntrySetThreadV1 { + let amt = match self { + JournalEntry::InitModuleV1 { wasm_hash } => { + serializer.serialize_value(&JournalEntryInitModuleV1 { wasm_hash }) + } + JournalEntry::UpdateMemoryRegionV1 { region, data } => { + serializer.serialize_value(&JournalEntryUpdateMemoryRegionV1 { + start: region.start, + end: region.end, + compressed_data: to_aligned_vec(compress_prepend_size(data.as_ref())), + }) + } + JournalEntry::ProcessExitV1 { exit_code } => { + serializer.serialize_value(&JournalEntryProcessExitV1 { + exit_code: exit_code.map(|e| e.into()), + }) + } + JournalEntry::SetThreadV1 { + id, + call_stack, + memory_stack, + store_data, + is_64bit, + } => serializer.serialize_value(&JournalEntrySetThreadV1 { + id, + call_stack: to_aligned_vec(call_stack), + memory_stack: to_aligned_vec(memory_stack), + store_data: to_aligned_vec(store_data), + is_64bit, + }), + JournalEntry::CloseThreadV1 { id, exit_code } => { + serializer.serialize_value(&JournalEntryCloseThreadV1 { id, - call_stack: to_aligned_vec(call_stack), - memory_stack: to_aligned_vec(memory_stack), - store_data: to_aligned_vec(store_data), - is_64bit, - }), - JournalEntry::CloseThreadV1 { id, exit_code } => { - serializer.serialize_value(&JournalEntryCloseThreadV1 { - id, - exit_code: exit_code.map(|e| e.into()), - }) - } - JournalEntry::FileDescriptorSeekV1 { fd, offset, whence } => serializer - .serialize_value(&JournalEntryFileDescriptorSeekV1 { - fd, - offset, - whence: whence.into(), - }), - JournalEntry::FileDescriptorWriteV1 { - fd, - offset, - data, - is_64bit, - } => serializer.serialize_value(&JournalEntryFileDescriptorWriteV1 { + exit_code: exit_code.map(|e| e.into()), + }) + } + JournalEntry::FileDescriptorSeekV1 { fd, offset, whence } => serializer + .serialize_value(&JournalEntryFileDescriptorSeekV1 { fd, offset, - data: to_aligned_vec(data), - is_64bit, + whence: whence.into(), }), - JournalEntry::SetClockTimeV1 { clock_id, time } => { - serializer.serialize_value(&JournalEntrySetClockTimeV1 { - clock_id: clock_id.into(), - time, - }) - } - JournalEntry::CloseFileDescriptorV1 { fd } => serializer - .serialize_value(&JournalEntryCloseFileDescriptorV1 { fd, _padding: 0 }), - JournalEntry::OpenFileDescriptorV1 { - fd, - dirfd, - dirflags, - path, - o_flags, - fs_rights_base, - fs_rights_inheriting, - fs_flags, - } => serializer.serialize_value(&JournalEntryOpenFileDescriptorV1 { + JournalEntry::FileDescriptorWriteV1 { + fd, + offset, + data, + is_64bit, + } => serializer.serialize_value(&JournalEntryFileDescriptorWriteV1 { + fd, + offset, + data: to_aligned_vec(data), + is_64bit, + }), + JournalEntry::SetClockTimeV1 { clock_id, time } => { + serializer.serialize_value(&JournalEntrySetClockTimeV1 { + clock_id: clock_id.into(), + time, + }) + } + JournalEntry::CloseFileDescriptorV1 { fd } => { + serializer.serialize_value(&JournalEntryCloseFileDescriptorV1 { fd }) + } + JournalEntry::OpenFileDescriptorV1 { + fd, + dirfd, + dirflags, + path, + o_flags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + } => serializer.serialize_value(&JournalEntryOpenFileDescriptorV1 { + fd, + dirfd, + dirflags, + path: to_aligned_vec(path.as_bytes()), + o_flags: o_flags.bits(), + fs_rights_base: fs_rights_base.bits(), + fs_rights_inheriting: fs_rights_inheriting.bits(), + fs_flags: fs_flags.bits(), + }), + JournalEntry::RenumberFileDescriptorV1 { old_fd, new_fd } => { + serializer.serialize_value(&JournalEntryRenumberFileDescriptorV1 { old_fd, new_fd }) + } + JournalEntry::DuplicateFileDescriptorV1 { + original_fd, + copied_fd, + } => serializer.serialize_value(&JournalEntryDuplicateFileDescriptorV1 { + original_fd, + copied_fd, + }), + JournalEntry::CreateDirectoryV1 { fd, path } => { + serializer.serialize_value(&JournalEntryCreateDirectoryV1 { fd, - dirfd, - dirflags, path: to_aligned_vec(path.as_bytes()), - o_flags: o_flags.bits(), - fs_rights_base: fs_rights_base.bits(), - fs_rights_inheriting: fs_rights_inheriting.bits(), - fs_flags: fs_flags.bits(), - }), - JournalEntry::RenumberFileDescriptorV1 { old_fd, new_fd } => serializer - .serialize_value(&JournalEntryRenumberFileDescriptorV1 { old_fd, new_fd }), - JournalEntry::DuplicateFileDescriptorV1 { - original_fd, - copied_fd, - } => serializer.serialize_value(&JournalEntryDuplicateFileDescriptorV1 { - original_fd, - copied_fd, - }), - JournalEntry::CreateDirectoryV1 { fd, path } => { - serializer.serialize_value(&JournalEntryCreateDirectoryV1 { - fd, - path: to_aligned_vec(path.as_bytes()), - _padding1: 0, - _padding2: 0, - }) - } - JournalEntry::RemoveDirectoryV1 { fd, path } => { - serializer.serialize_value(&JournalEntryRemoveDirectoryV1 { - fd, - path: to_aligned_vec(path.as_bytes()), - _padding: 0, - }) - } - JournalEntry::PathSetTimesV1 { - fd, - flags, - path, - st_atim, - st_mtim, - fst_flags, - } => serializer.serialize_value(&JournalEntryPathSetTimesV1 { + }) + } + JournalEntry::RemoveDirectoryV1 { fd, path } => { + serializer.serialize_value(&JournalEntryRemoveDirectoryV1 { fd, - flags, path: to_aligned_vec(path.as_bytes()), - st_atim, - st_mtim, - fst_flags: fst_flags.bits(), - _padding1: 0, - _padding2: 0, - }), - JournalEntry::FileDescriptorSetTimesV1 { - fd, - st_atim, - st_mtim, - fst_flags, - } => serializer.serialize_value(&JournalEntryFileDescriptorSetTimesV1 { - fd, - st_atim, - st_mtim, - fst_flags: fst_flags.bits(), - _padding: 0, - }), - JournalEntry::FileDescriptorSetFlagsV1 { fd, flags } => { - serializer.serialize_value(&JournalEntryFileDescriptorSetFlagsV1 { - fd, - flags: flags.bits(), - _padding: 0, - }) - } - JournalEntry::FileDescriptorSetRightsV1 { - fd, - fs_rights_base, - fs_rights_inheriting, - } => serializer.serialize_value(&JournalEntryFileDescriptorSetRightsV1 { - fd, - fs_rights_base: fs_rights_base.bits(), - fs_rights_inheriting: fs_rights_inheriting.bits(), - }), - JournalEntry::FileDescriptorSetSizeV1 { fd, st_size } => serializer - .serialize_value(&JournalEntryFileDescriptorSetSizeV1 { - fd, - st_size, - _padding: 0, - }), - JournalEntry::FileDescriptorAdviseV1 { - fd, - offset, - len, - advice, - } => serializer.serialize_value(&JournalEntryFileDescriptorAdviseV1 { - fd, - offset, - len, - advice: advice.into(), - }), - JournalEntry::FileDescriptorAllocateV1 { fd, offset, len } => serializer - .serialize_value(&JournalEntryFileDescriptorAllocateV1 { - fd, - offset, - len, - _padding: 0, - }), - JournalEntry::CreateHardLinkV1 { - old_fd, - old_path, - old_flags, - new_fd, - new_path, - } => serializer.serialize_value(&JournalEntryCreateHardLinkV1 { - old_fd, - old_path: to_aligned_vec(old_path.as_bytes()), - old_flags, - new_fd, - new_path: to_aligned_vec(new_path.as_bytes()), - _padding: 0, - }), - JournalEntry::CreateSymbolicLinkV1 { - old_path, - fd, - new_path, - } => serializer.serialize_value(&JournalEntryCreateSymbolicLinkV1 { - old_path: to_aligned_vec(old_path.as_bytes()), - fd, - _padding: 0, - new_path: to_aligned_vec(new_path.as_bytes()), - }), - JournalEntry::UnlinkFileV1 { fd, path } => { - serializer.serialize_value(&JournalEntryUnlinkFileV1 { - fd, - path: to_aligned_vec(path.as_bytes()), - _padding: 0, - }) - } - JournalEntry::PathRenameV1 { - old_fd, - old_path, - new_fd, - new_path, - } => serializer.serialize_value(&JournalEntryPathRenameV1 { - old_fd, - old_path: to_aligned_vec_str(old_path), - new_fd, - new_path: to_aligned_vec_str(new_path), - }), - JournalEntry::ChangeDirectoryV1 { path } => { - serializer.serialize_value(&JournalEntryChangeDirectoryV1 { - path: to_aligned_vec_str(path), - }) - } - JournalEntry::EpollCreateV1 { fd } => { - serializer.serialize_value(&JournalEntryEpollCreateV1 { fd, _padding: 0 }) - } - JournalEntry::EpollCtlV1 { - epfd, - op, - fd, - event, - } => serializer.serialize_value(&JournalEntryEpollCtlV1 { - epfd, - op: op.into(), - fd, - event: event.map(|e| e.into()), - }), - JournalEntry::TtySetV1 { tty, line_feeds } => { - serializer.serialize_value(&JournalEntryTtySetV1 { - cols: tty.cols, - rows: tty.rows, - width: tty.width, - height: tty.height, - stdin_tty: tty.stdin_tty, - stdout_tty: tty.stdout_tty, - stderr_tty: tty.stderr_tty, - echo: tty.echo, - line_buffered: tty.line_buffered, - line_feeds, - }) - } - JournalEntry::CreatePipeV1 { fd1, fd2 } => { - serializer.serialize_value(&JournalEntryCreatePipeV1 { fd1, fd2 }) - } - JournalEntry::CreateEventV1 { - initial_val, - flags, - fd, - } => serializer.serialize_value(&JournalEntryCreateEventV1 { - initial_val, - flags, - fd, - _padding: 0, - }), - JournalEntry::PortAddAddrV1 { cidr } => { - serializer.serialize_value(&JournalEntryPortAddAddrV1 { cidr: cidr.into() }) - } - JournalEntry::PortDelAddrV1 { addr } => { - serializer.serialize_value(&JournalEntryPortDelAddrV1 { addr }) - } - JournalEntry::PortAddrClearV1 => serializer.serialize_value(&()), - JournalEntry::PortBridgeV1 { - network, - token, - security, - } => serializer.serialize_value(&JournalEntryPortBridgeV1 { - network: to_aligned_vec_str(network), - token: to_aligned_vec_str(token), - security: security.into(), - }), - JournalEntry::PortUnbridgeV1 => serializer.serialize_value(&()), - JournalEntry::PortDhcpAcquireV1 => serializer.serialize_value(&()), - JournalEntry::PortGatewaySetV1 { ip } => { - serializer.serialize_value(&JournalEntryPortGatewaySetV1 { ip }) - } - JournalEntry::PortRouteAddV1 { - cidr, - via_router, - preferred_until, - expires_at, - } => serializer.serialize_value(&JournalEntryPortRouteAddV1 { - cidr: cidr.into(), - via_router, - preferred_until, - expires_at, - }), - JournalEntry::PortRouteClearV1 => serializer.serialize_value(&()), - JournalEntry::PortRouteDelV1 { ip } => { - serializer.serialize_value(&JournalEntryPortRouteDelV1 { ip }) - } - JournalEntry::SocketOpenV1 { af, ty, pt, fd } => { - serializer.serialize_value(&JournalEntrySocketOpenV1 { - af: af.into(), - ty: ty.into(), - pt: pt.into(), - fd, - }) - } - JournalEntry::SocketListenV1 { fd, backlog } => { - serializer.serialize_value(&JournalEntrySocketListenV1 { fd, backlog }) - } - JournalEntry::SocketBindV1 { fd, addr } => { - serializer.serialize_value(&JournalEntrySocketBindV1 { - fd, - addr, - _padding: 0, - }) - } - JournalEntry::SocketConnectedV1 { fd, addr } => { - serializer.serialize_value(&JournalEntrySocketConnectedV1 { - fd, - addr, - _padding: 0, - }) - } - JournalEntry::SocketAcceptedV1 { - listen_fd, - fd, - peer_addr, - fd_flags, - non_blocking: nonblocking, - } => serializer.serialize_value(&JournalEntrySocketAcceptedV1 { - listen_fd, - fd, - peer_addr, - fd_flags: fd_flags.bits(), - nonblocking, - }), - JournalEntry::SocketJoinIpv4MulticastV1 { - fd, - multiaddr, - iface, - } => serializer.serialize_value(&JournalEntrySocketJoinIpv4MulticastV1 { - fd, - multiaddr, - iface, - }), - JournalEntry::SocketJoinIpv6MulticastV1 { - fd, - multi_addr: multiaddr, - iface, - } => serializer.serialize_value(&JournalEntrySocketJoinIpv6MulticastV1 { - fd, - multiaddr, - iface, - }), - JournalEntry::SocketLeaveIpv4MulticastV1 { - fd, - multi_addr: multiaddr, - iface, - } => serializer.serialize_value(&JournalEntrySocketLeaveIpv4MulticastV1 { + }) + } + JournalEntry::PathSetTimesV1 { + fd, + flags, + path, + st_atim, + st_mtim, + fst_flags, + } => serializer.serialize_value(&JournalEntryPathSetTimesV1 { + fd, + flags, + path: to_aligned_vec(path.as_bytes()), + st_atim, + st_mtim, + fst_flags: fst_flags.bits(), + }), + JournalEntry::FileDescriptorSetTimesV1 { + fd, + st_atim, + st_mtim, + fst_flags, + } => serializer.serialize_value(&JournalEntryFileDescriptorSetTimesV1 { + fd, + st_atim, + st_mtim, + fst_flags: fst_flags.bits(), + }), + JournalEntry::FileDescriptorSetFlagsV1 { fd, flags } => { + serializer.serialize_value(&JournalEntryFileDescriptorSetFlagsV1 { fd, - multiaddr, - iface, - }), - JournalEntry::SocketLeaveIpv6MulticastV1 { + flags: flags.bits(), + }) + } + JournalEntry::FileDescriptorSetRightsV1 { + fd, + fs_rights_base, + fs_rights_inheriting, + } => serializer.serialize_value(&JournalEntryFileDescriptorSetRightsV1 { + fd, + fs_rights_base: fs_rights_base.bits(), + fs_rights_inheriting: fs_rights_inheriting.bits(), + }), + JournalEntry::FileDescriptorSetSizeV1 { fd, st_size } => { + serializer.serialize_value(&JournalEntryFileDescriptorSetSizeV1 { fd, st_size }) + } + JournalEntry::FileDescriptorAdviseV1 { + fd, + offset, + len, + advice, + } => serializer.serialize_value(&JournalEntryFileDescriptorAdviseV1 { + fd, + offset, + len, + advice: advice.into(), + }), + JournalEntry::FileDescriptorAllocateV1 { fd, offset, len } => serializer + .serialize_value(&JournalEntryFileDescriptorAllocateV1 { fd, offset, len }), + JournalEntry::CreateHardLinkV1 { + old_fd, + old_path, + old_flags, + new_fd, + new_path, + } => serializer.serialize_value(&JournalEntryCreateHardLinkV1 { + old_fd, + old_path: to_aligned_vec(old_path.as_bytes()), + old_flags, + new_fd, + new_path: to_aligned_vec(new_path.as_bytes()), + }), + JournalEntry::CreateSymbolicLinkV1 { + old_path, + fd, + new_path, + } => serializer.serialize_value(&JournalEntryCreateSymbolicLinkV1 { + old_path: to_aligned_vec(old_path.as_bytes()), + fd, + new_path: to_aligned_vec(new_path.as_bytes()), + }), + JournalEntry::UnlinkFileV1 { fd, path } => { + serializer.serialize_value(&JournalEntryUnlinkFileV1 { fd, - multi_addr: multiaddr, - iface, - } => serializer.serialize_value(&JournalEntrySocketLeaveIpv6MulticastV1 { + path: to_aligned_vec(path.as_bytes()), + }) + } + JournalEntry::PathRenameV1 { + old_fd, + old_path, + new_fd, + new_path, + } => serializer.serialize_value(&JournalEntryPathRenameV1 { + old_fd, + old_path: to_aligned_vec_str(old_path), + new_fd, + new_path: to_aligned_vec_str(new_path), + }), + JournalEntry::ChangeDirectoryV1 { path } => { + serializer.serialize_value(&JournalEntryChangeDirectoryV1 { + path: to_aligned_vec_str(path), + }) + } + JournalEntry::EpollCreateV1 { fd } => { + serializer.serialize_value(&JournalEntryEpollCreateV1 { fd }) + } + JournalEntry::EpollCtlV1 { + epfd, + op, + fd, + event, + } => serializer.serialize_value(&JournalEntryEpollCtlV1 { + epfd, + op: op.into(), + fd, + event: event.map(|e| e.into()), + }), + JournalEntry::TtySetV1 { tty, line_feeds } => { + serializer.serialize_value(&JournalEntryTtySetV1 { + cols: tty.cols, + rows: tty.rows, + width: tty.width, + height: tty.height, + stdin_tty: tty.stdin_tty, + stdout_tty: tty.stdout_tty, + stderr_tty: tty.stderr_tty, + echo: tty.echo, + line_buffered: tty.line_buffered, + line_feeds, + }) + } + JournalEntry::CreatePipeV1 { fd1, fd2 } => { + serializer.serialize_value(&JournalEntryCreatePipeV1 { fd1, fd2 }) + } + JournalEntry::CreateEventV1 { + initial_val, + flags, + fd, + } => serializer.serialize_value(&JournalEntryCreateEventV1 { + initial_val, + flags, + fd, + }), + JournalEntry::PortAddAddrV1 { cidr } => { + serializer.serialize_value(&JournalEntryPortAddAddrV1 { cidr: cidr.into() }) + } + JournalEntry::PortDelAddrV1 { addr } => { + serializer.serialize_value(&JournalEntryPortDelAddrV1 { addr }) + } + JournalEntry::PortAddrClearV1 => serializer.serialize_value(&()), + JournalEntry::PortBridgeV1 { + network, + token, + security, + } => serializer.serialize_value(&JournalEntryPortBridgeV1 { + network: to_aligned_vec_str(network), + token: to_aligned_vec_str(token), + security: security.into(), + }), + JournalEntry::PortUnbridgeV1 => serializer.serialize_value(&()), + JournalEntry::PortDhcpAcquireV1 => serializer.serialize_value(&()), + JournalEntry::PortGatewaySetV1 { ip } => { + serializer.serialize_value(&JournalEntryPortGatewaySetV1 { ip }) + } + JournalEntry::PortRouteAddV1 { + cidr, + via_router, + preferred_until, + expires_at, + } => serializer.serialize_value(&JournalEntryPortRouteAddV1 { + cidr: cidr.into(), + via_router, + preferred_until, + expires_at, + }), + JournalEntry::PortRouteClearV1 => serializer.serialize_value(&()), + JournalEntry::PortRouteDelV1 { ip } => { + serializer.serialize_value(&JournalEntryPortRouteDelV1 { ip }) + } + JournalEntry::SocketOpenV1 { af, ty, pt, fd } => { + serializer.serialize_value(&JournalEntrySocketOpenV1 { + af: af.into(), + ty: ty.into(), + pt: pt.into(), fd, - multiaddr, - iface, - }), - JournalEntry::SocketSendFileV1 { - socket_fd, - file_fd, - offset, - count, - } => serializer.serialize_value(&JournalEntrySocketSendFileV1 { - socket_fd, - file_fd, - offset, - count, - }), - JournalEntry::SocketSendToV1 { + }) + } + JournalEntry::SocketListenV1 { fd, backlog } => { + serializer.serialize_value(&JournalEntrySocketListenV1 { fd, backlog }) + } + JournalEntry::SocketBindV1 { fd, addr } => { + serializer.serialize_value(&JournalEntrySocketBindV1 { fd, addr }) + } + JournalEntry::SocketConnectedV1 { fd, addr } => { + serializer.serialize_value(&JournalEntrySocketConnectedV1 { fd, addr }) + } + JournalEntry::SocketAcceptedV1 { + listen_fd, + fd, + peer_addr, + fd_flags, + non_blocking: nonblocking, + } => serializer.serialize_value(&JournalEntrySocketAcceptedV1 { + listen_fd, + fd, + peer_addr, + fd_flags: fd_flags.bits(), + nonblocking, + }), + JournalEntry::SocketJoinIpv4MulticastV1 { + fd, + multiaddr, + iface, + } => serializer.serialize_value(&JournalEntrySocketJoinIpv4MulticastV1 { + fd, + multiaddr, + iface, + }), + JournalEntry::SocketJoinIpv6MulticastV1 { + fd, + multi_addr: multiaddr, + iface, + } => serializer.serialize_value(&JournalEntrySocketJoinIpv6MulticastV1 { + fd, + multiaddr, + iface, + }), + JournalEntry::SocketLeaveIpv4MulticastV1 { + fd, + multi_addr: multiaddr, + iface, + } => serializer.serialize_value(&JournalEntrySocketLeaveIpv4MulticastV1 { + fd, + multiaddr, + iface, + }), + JournalEntry::SocketLeaveIpv6MulticastV1 { + fd, + multi_addr: multiaddr, + iface, + } => serializer.serialize_value(&JournalEntrySocketLeaveIpv6MulticastV1 { + fd, + multiaddr, + iface, + }), + JournalEntry::SocketSendFileV1 { + socket_fd, + file_fd, + offset, + count, + } => serializer.serialize_value(&JournalEntrySocketSendFileV1 { + socket_fd, + file_fd, + offset, + count, + }), + JournalEntry::SocketSendToV1 { + fd, + data, + flags, + addr, + is_64bit, + } => serializer.serialize_value(&JournalEntrySocketSendToV1 { + fd, + data: to_aligned_vec(data), + flags, + addr, + is_64bit, + }), + JournalEntry::SocketSendV1 { + fd, + data, + flags, + is_64bit, + } => serializer.serialize_value(&JournalEntrySocketSendV1 { + fd, + data: to_aligned_vec(data), + flags, + is_64bit, + }), + JournalEntry::SocketSetOptFlagV1 { fd, opt, flag } => { + serializer.serialize_value(&JournalEntrySocketSetOptFlagV1 { fd, - data, - flags, - addr, - is_64bit, - } => serializer.serialize_value(&JournalEntrySocketSendToV1 { + opt: opt.into(), + flag, + }) + } + JournalEntry::SocketSetOptSizeV1 { fd, opt, size } => { + serializer.serialize_value(&JournalEntrySocketSetOptSizeV1 { fd, - data: to_aligned_vec(data), - flags, - addr, - is_64bit, - }), - JournalEntry::SocketSendV1 { + opt: opt.into(), + size, + }) + } + JournalEntry::SocketSetOptTimeV1 { fd, ty, time } => { + serializer.serialize_value(&JournalEntrySocketSetOptTimeV1 { fd, - data, - flags, - is_64bit, - } => serializer.serialize_value(&JournalEntrySocketSendV1 { + ty: ty.into(), + time, + }) + } + JournalEntry::SocketShutdownV1 { fd, how } => { + serializer.serialize_value(&JournalEntrySocketShutdownV1 { fd, - data: to_aligned_vec(data), - flags, - is_64bit, - }), - JournalEntry::SocketSetOptFlagV1 { fd, opt, flag } => { - serializer.serialize_value(&JournalEntrySocketSetOptFlagV1 { - fd, - opt: opt.into(), - flag, - }) - } - JournalEntry::SocketSetOptSizeV1 { fd, opt, size } => { - serializer.serialize_value(&JournalEntrySocketSetOptSizeV1 { - fd, - opt: opt.into(), - size, - }) - } - JournalEntry::SocketSetOptTimeV1 { fd, ty, time } => { - serializer.serialize_value(&JournalEntrySocketSetOptTimeV1 { - fd, - ty: ty.into(), - time, - }) - } - JournalEntry::SocketShutdownV1 { fd, how } => { - serializer.serialize_value(&JournalEntrySocketShutdownV1 { - fd, - how: how.into(), - }) - } - JournalEntry::SnapshotV1 { when, trigger } => { - serializer.serialize_value(&JournalEntrySnapshotV1 { - since_epoch: when - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap_or(Duration::ZERO), - trigger: trigger.into(), - }) - } - } - .map_err(|err| anyhow::format_err!("failed to serialize journal record - {}", err))?; + how: how.into(), + }) + } + JournalEntry::SnapshotV1 { when, trigger } => { + serializer.serialize_value(&JournalEntrySnapshotV1 { + since_epoch: when + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap_or(Duration::ZERO), + trigger: trigger.into(), + }) + } + } + .map_err(|err| anyhow::format_err!("failed to serialize journal record - {}", err))?; Ok(amt) } } @@ -976,7 +949,7 @@ pub enum ArchivedJournalEntry<'a> { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryInitModuleV1 { pub wasm_hash: [u8; 8], } @@ -984,16 +957,15 @@ pub struct JournalEntryInitModuleV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryProcessExitV1 { pub exit_code: Option, - pub _padding: u32, } #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySetThreadV1 { pub id: u32, pub call_stack: AlignedVec, @@ -1005,7 +977,7 @@ pub struct JournalEntrySetThreadV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryCloseThreadV1 { pub id: u32, pub exit_code: Option, @@ -1014,7 +986,7 @@ pub struct JournalEntryCloseThreadV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryFileDescriptorSeekV1 { pub fd: u32, pub whence: JournalWhenceV1, @@ -1024,7 +996,7 @@ pub struct JournalEntryFileDescriptorSeekV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryFileDescriptorWriteV1 { pub data: AlignedVec, pub offset: u64, @@ -1035,7 +1007,7 @@ pub struct JournalEntryFileDescriptorWriteV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryUpdateMemoryRegionV1 { pub compressed_data: AlignedVec, pub start: u64, @@ -1045,7 +1017,7 @@ pub struct JournalEntryUpdateMemoryRegionV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySetClockTimeV1 { pub clock_id: JournalSnapshot0ClockidV1, pub time: u64, @@ -1054,7 +1026,7 @@ pub struct JournalEntrySetClockTimeV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryOpenFileDescriptorV1 { pub fd: u32, pub dirfd: u32, @@ -1069,16 +1041,15 @@ pub struct JournalEntryOpenFileDescriptorV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryCloseFileDescriptorV1 { pub fd: u32, - pub _padding: u32, } #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryRenumberFileDescriptorV1 { pub old_fd: u32, pub new_fd: u32, @@ -1087,7 +1058,7 @@ pub struct JournalEntryRenumberFileDescriptorV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryDuplicateFileDescriptorV1 { pub original_fd: u32, pub copied_fd: u32, @@ -1096,28 +1067,25 @@ pub struct JournalEntryDuplicateFileDescriptorV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryCreateDirectoryV1 { pub fd: u32, - pub _padding1: u32, - pub _padding2: u64, pub path: AlignedVec, } #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryRemoveDirectoryV1 { pub fd: u32, - pub _padding: u32, pub path: AlignedVec, } #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryPathSetTimesV1 { pub fd: u32, pub flags: u32, @@ -1125,18 +1093,15 @@ pub struct JournalEntryPathSetTimesV1 { pub st_atim: u64, pub st_mtim: u64, pub fst_flags: u16, - pub _padding1: u16, - pub _padding2: u32, } #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryFileDescriptorSetTimesV1 { pub fd: u32, pub fst_flags: u16, - pub _padding: u16, pub st_atim: u64, pub st_mtim: u64, } @@ -1144,27 +1109,25 @@ pub struct JournalEntryFileDescriptorSetTimesV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryFileDescriptorSetSizeV1 { pub fd: u32, - pub _padding: u32, pub st_size: u64, } #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryFileDescriptorSetFlagsV1 { pub fd: u32, pub flags: u16, - pub _padding: u16, } #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryFileDescriptorSetRightsV1 { pub fd: u32, pub fs_rights_base: u64, @@ -1174,7 +1137,7 @@ pub struct JournalEntryFileDescriptorSetRightsV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryFileDescriptorAdviseV1 { pub fd: u32, pub offset: u64, @@ -1185,10 +1148,9 @@ pub struct JournalEntryFileDescriptorAdviseV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryFileDescriptorAllocateV1 { pub fd: u32, - pub _padding: u32, pub offset: u64, pub len: u64, } @@ -1196,10 +1158,9 @@ pub struct JournalEntryFileDescriptorAllocateV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryCreateHardLinkV1 { pub old_fd: u32, - pub _padding: u32, pub old_path: AlignedVec, pub old_flags: u32, pub new_fd: u32, @@ -1209,10 +1170,9 @@ pub struct JournalEntryCreateHardLinkV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryCreateSymbolicLinkV1 { pub fd: u32, - pub _padding: u32, pub old_path: AlignedVec, pub new_path: AlignedVec, } @@ -1220,17 +1180,16 @@ pub struct JournalEntryCreateSymbolicLinkV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryUnlinkFileV1 { pub fd: u32, - pub _padding: u32, pub path: AlignedVec, } #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryPathRenameV1 { pub old_fd: u32, pub old_path: AlignedVec, @@ -1241,7 +1200,7 @@ pub struct JournalEntryPathRenameV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryChangeDirectoryV1 { pub path: AlignedVec, } @@ -1249,16 +1208,15 @@ pub struct JournalEntryChangeDirectoryV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryEpollCreateV1 { pub fd: u32, - pub _padding: u32, } #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryEpollCtlV1 { pub epfd: u32, pub op: JournalEpollCtlV1, @@ -1269,7 +1227,7 @@ pub struct JournalEntryEpollCtlV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryTtySetV1 { pub cols: u32, pub rows: u32, @@ -1286,7 +1244,7 @@ pub struct JournalEntryTtySetV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryCreatePipeV1 { pub fd1: u32, pub fd2: u32, @@ -1295,18 +1253,17 @@ pub struct JournalEntryCreatePipeV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryCreateEventV1 { pub initial_val: u64, pub flags: u16, - pub _padding: u16, pub fd: u32, } #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryPortAddAddrV1 { pub cidr: JournalIpCidrV1, } @@ -1314,7 +1271,7 @@ pub struct JournalEntryPortAddAddrV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryPortDelAddrV1 { pub addr: IpAddr, } @@ -1322,7 +1279,7 @@ pub struct JournalEntryPortDelAddrV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryPortBridgeV1 { pub network: AlignedVec, pub token: AlignedVec, @@ -1332,7 +1289,7 @@ pub struct JournalEntryPortBridgeV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryPortGatewaySetV1 { pub ip: IpAddr, } @@ -1340,7 +1297,7 @@ pub struct JournalEntryPortGatewaySetV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryPortRouteAddV1 { pub cidr: JournalIpCidrV1, pub via_router: IpAddr, @@ -1351,7 +1308,7 @@ pub struct JournalEntryPortRouteAddV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryPortRouteDelV1 { pub ip: IpAddr, } @@ -1359,7 +1316,7 @@ pub struct JournalEntryPortRouteDelV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySocketOpenV1 { pub af: JournalAddressfamilyV1, pub ty: JournalSocktypeV1, @@ -1370,7 +1327,7 @@ pub struct JournalEntrySocketOpenV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySocketListenV1 { pub fd: u32, pub backlog: u32, @@ -1379,27 +1336,25 @@ pub struct JournalEntrySocketListenV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySocketBindV1 { pub fd: u32, - pub _padding: u32, pub addr: SocketAddr, } #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySocketConnectedV1 { pub fd: u32, - pub _padding: u32, pub addr: SocketAddr, } #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySocketAcceptedV1 { pub listen_fd: u32, pub fd: u32, @@ -1411,7 +1366,7 @@ pub struct JournalEntrySocketAcceptedV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySocketJoinIpv4MulticastV1 { pub fd: u32, pub multiaddr: Ipv4Addr, @@ -1421,7 +1376,7 @@ pub struct JournalEntrySocketJoinIpv4MulticastV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySocketJoinIpv6MulticastV1 { pub fd: u32, pub multiaddr: Ipv6Addr, @@ -1431,7 +1386,7 @@ pub struct JournalEntrySocketJoinIpv6MulticastV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySocketLeaveIpv4MulticastV1 { pub fd: u32, pub multiaddr: Ipv4Addr, @@ -1441,7 +1396,7 @@ pub struct JournalEntrySocketLeaveIpv4MulticastV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySocketLeaveIpv6MulticastV1 { pub fd: u32, pub multiaddr: Ipv6Addr, @@ -1451,7 +1406,7 @@ pub struct JournalEntrySocketLeaveIpv6MulticastV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySocketSendFileV1 { pub socket_fd: u32, pub file_fd: u32, @@ -1462,7 +1417,7 @@ pub struct JournalEntrySocketSendFileV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySocketSendToV1 { pub fd: u32, pub data: AlignedVec, @@ -1474,7 +1429,7 @@ pub struct JournalEntrySocketSendToV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySocketSendV1 { pub fd: u32, pub data: AlignedVec, @@ -1485,7 +1440,7 @@ pub struct JournalEntrySocketSendV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySocketSetOptFlagV1 { pub fd: u32, pub opt: JournalSockoptionV1, @@ -1495,7 +1450,7 @@ pub struct JournalEntrySocketSetOptFlagV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySocketSetOptSizeV1 { pub fd: u32, pub opt: JournalSockoptionV1, @@ -1505,7 +1460,7 @@ pub struct JournalEntrySocketSetOptSizeV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySocketSetOptTimeV1 { pub fd: u32, pub ty: JournalTimeTypeV1, @@ -1515,7 +1470,7 @@ pub struct JournalEntrySocketSetOptTimeV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySocketShutdownV1 { pub fd: u32, pub how: JournalSocketShutdownV1, @@ -1524,7 +1479,7 @@ pub struct JournalEntrySocketShutdownV1 { #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] +#[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySnapshotV1 { pub since_epoch: Duration, pub trigger: JournalSnapshotTriggerV1, diff --git a/lib/journal/src/concrete/archived_from.rs b/lib/journal/src/concrete/archived_from.rs index 938fab72ff2..aaf68faeb9d 100644 --- a/lib/journal/src/concrete/archived_from.rs +++ b/lib/journal/src/concrete/archived_from.rs @@ -591,7 +591,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { }, ArchivedJournalEntry::ProcessExitV1(ArchivedJournalEntryProcessExitV1 { exit_code, - _padding: _, }) => Self::ProcessExitV1 { exit_code: exit_code.as_ref().map(|code| code.into()), }, @@ -661,24 +660,21 @@ impl<'a> TryFrom> for JournalEntry<'a> { fs_flags: wasi::Fdflags::from_bits_truncate(*fs_flags), }, ArchivedJournalEntry::CloseFileDescriptorV1( - ArchivedJournalEntryCloseFileDescriptorV1 { fd, _padding: _ }, + ArchivedJournalEntryCloseFileDescriptorV1 { fd }, ) => Self::CloseFileDescriptorV1 { fd: *fd }, ArchivedJournalEntry::RemoveDirectoryV1(ArchivedJournalEntryRemoveDirectoryV1 { fd, path, - _padding: _, }) => Self::RemoveDirectoryV1 { fd: *fd, path: String::from_utf8_lossy(path.as_ref()), }, - ArchivedJournalEntry::UnlinkFileV1(ArchivedJournalEntryUnlinkFileV1 { - fd, - path, - _padding: _, - }) => Self::UnlinkFileV1 { - fd: *fd, - path: String::from_utf8_lossy(path.as_ref()), - }, + ArchivedJournalEntry::UnlinkFileV1(ArchivedJournalEntryUnlinkFileV1 { fd, path }) => { + Self::UnlinkFileV1 { + fd: *fd, + path: String::from_utf8_lossy(path.as_ref()), + } + } ArchivedJournalEntry::PathRenameV1(ArchivedJournalEntryPathRenameV1 { old_fd, old_path, @@ -724,8 +720,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { ArchivedJournalEntry::CreateDirectoryV1(ArchivedJournalEntryCreateDirectoryV1 { fd, path, - _padding1: _, - _padding2: _, }) => Self::CreateDirectoryV1 { fd: *fd, path: String::from_utf8_lossy(path.as_ref()), @@ -737,8 +731,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { st_atim, st_mtim, fst_flags, - _padding1: _, - _padding2: _, }) => Self::PathSetTimesV1 { fd: *fd, path: String::from_utf8_lossy(path.as_ref()), @@ -753,7 +745,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { st_atim, st_mtim, fst_flags, - _padding: _, }, ) => Self::FileDescriptorSetTimesV1 { fd: *fd, @@ -762,21 +753,13 @@ impl<'a> TryFrom> for JournalEntry<'a> { fst_flags: wasi::Fstflags::from_bits_truncate(*fst_flags), }, ArchivedJournalEntry::FileDescriptorSetSizeV1( - ArchivedJournalEntryFileDescriptorSetSizeV1 { - fd, - st_size, - _padding: _, - }, + ArchivedJournalEntryFileDescriptorSetSizeV1 { fd, st_size }, ) => Self::FileDescriptorSetSizeV1 { fd: *fd, st_size: *st_size, }, ArchivedJournalEntry::FileDescriptorSetFlagsV1( - ArchivedJournalEntryFileDescriptorSetFlagsV1 { - fd, - flags, - _padding: _, - }, + ArchivedJournalEntryFileDescriptorSetFlagsV1 { fd, flags }, ) => Self::FileDescriptorSetFlagsV1 { fd: *fd, flags: wasi::Fdflags::from_bits_truncate(*flags), @@ -806,12 +789,7 @@ impl<'a> TryFrom> for JournalEntry<'a> { advice: advice.into(), }, ArchivedJournalEntry::FileDescriptorAllocateV1( - ArchivedJournalEntryFileDescriptorAllocateV1 { - fd, - offset, - len, - _padding: _, - }, + ArchivedJournalEntryFileDescriptorAllocateV1 { fd, offset, len }, ) => Self::FileDescriptorAllocateV1 { fd: *fd, offset: *offset, @@ -823,7 +801,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { old_flags, new_fd, new_path, - _padding: _, }) => Self::CreateHardLinkV1 { old_fd: *old_fd, old_path: String::from_utf8_lossy(old_path.as_ref()), @@ -836,7 +813,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { old_path, fd, new_path, - _padding: _, }, ) => Self::CreateSymbolicLinkV1 { old_path: String::from_utf8_lossy(old_path.as_ref()), @@ -848,10 +824,9 @@ impl<'a> TryFrom> for JournalEntry<'a> { }) => Self::ChangeDirectoryV1 { path: String::from_utf8_lossy(path.as_ref()), }, - ArchivedJournalEntry::EpollCreateV1(ArchivedJournalEntryEpollCreateV1 { - fd, - _padding: _, - }) => Self::EpollCreateV1 { fd: *fd }, + ArchivedJournalEntry::EpollCreateV1(ArchivedJournalEntryEpollCreateV1 { fd }) => { + Self::EpollCreateV1 { fd: *fd } + } ArchivedJournalEntry::EpollCtlV1(ArchivedJournalEntryEpollCtlV1 { epfd, ref op, @@ -962,18 +937,15 @@ impl<'a> TryFrom> for JournalEntry<'a> { fd: *fd, backlog: *backlog, }, - ArchivedJournalEntry::SocketBindV1(ArchivedJournalEntrySocketBindV1 { - fd, - addr, - _padding: _, - }) => Self::SocketBindV1 { - fd: *fd, - addr: addr.as_socket_addr(), - }, + ArchivedJournalEntry::SocketBindV1(ArchivedJournalEntrySocketBindV1 { fd, addr }) => { + Self::SocketBindV1 { + fd: *fd, + addr: addr.as_socket_addr(), + } + } ArchivedJournalEntry::SocketConnectedV1(ArchivedJournalEntrySocketConnectedV1 { fd, addr, - _padding: _, }) => Self::SocketConnectedV1 { fd: *fd, addr: addr.as_socket_addr(), @@ -1108,7 +1080,6 @@ impl<'a> TryFrom> for JournalEntry<'a> { initial_val, flags, fd, - _padding: _, }) => Self::CreateEventV1 { initial_val: *initial_val, flags: *flags, From 2fe6edc8517cb30eddec535f80553de4ce26afdb Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Tue, 23 Jan 2024 05:33:54 +1100 Subject: [PATCH 17/67] Finished a mounting feature for the journals --- lib/cli/src/commands/journal/mount/fs.rs | 194 ++++++++++++++--------- lib/virtual-fs/src/mem_fs/file.rs | 2 + 2 files changed, 122 insertions(+), 74 deletions(-) diff --git a/lib/cli/src/commands/journal/mount/fs.rs b/lib/cli/src/commands/journal/mount/fs.rs index 8950c65cf3c..20efe656411 100644 --- a/lib/cli/src/commands/journal/mount/fs.rs +++ b/lib/cli/src/commands/journal/mount/fs.rs @@ -12,7 +12,7 @@ use std::{ use fuse::{ FileAttr, Filesystem, ReplyAttr, ReplyCreate, ReplyData, ReplyDirectory, ReplyEmpty, - ReplyEntry, ReplyOpen, Request, + ReplyEntry, ReplyOpen, ReplyWrite, Request, }; use tokio::runtime::Handle; use virtual_fs::{mem_fs, AsyncReadExt, AsyncSeekExt, AsyncWriteExt, FileSystem, FsError}; @@ -31,12 +31,11 @@ struct State { handle: tokio::runtime::Handle, mem_fs: mem_fs::FileSystem, inos: HashMap>, - fuse_lookup: HashMap>, - seed: WasiFdSeed, - journal_lookup: HashMap< + lookup: HashMap< u32, Arc>>, >, + seed: WasiFdSeed, } #[derive(Debug)] @@ -64,11 +63,10 @@ impl JournalFileSystem { mem_fs, inos: Default::default(), seed: fd_seed, - fuse_lookup: Default::default(), - journal_lookup: Default::default(), + lookup: Default::default(), }), }; - copy_journal(&journal, &state)?; + tokio::task::block_in_place(|| copy_journal(&journal, &state))?; let ret = Self { handle: tokio::runtime::Handle::current(), @@ -146,7 +144,7 @@ impl WritableJournal for MutexState { is_64bit, } => { let handle = state.handle.clone(); - if let Some(file) = state.journal_lookup.get_mut(&fd) { + if let Some(file) = state.lookup.get_mut(&fd) { handle.block_on(async { let mut file = file.lock().await; file.seek(io::SeekFrom::Start(offset)).await; @@ -155,7 +153,7 @@ impl WritableJournal for MutexState { } } JournalEntry::CloseFileDescriptorV1 { fd } => { - state.journal_lookup.remove(&fd); + state.lookup.remove(&fd); } JournalEntry::OpenFileDescriptorV1 { fd, @@ -173,15 +171,17 @@ impl WritableJournal for MutexState { .new_open_options() .create(o_flags.contains(Oflags::CREATE)) .truncate(o_flags.contains(Oflags::TRUNC)) + .write(true) + .read(true) .open(path.as_ref())?; state - .journal_lookup + .lookup .insert(fd, Arc::new(tokio::sync::Mutex::new(file))); } JournalEntry::RenumberFileDescriptorV1 { old_fd, new_fd } => { state.seed.clip_val(new_fd + 1); - if let Some(file) = state.journal_lookup.remove(&old_fd) { - state.journal_lookup.insert(new_fd, file); + if let Some(file) = state.lookup.remove(&old_fd) { + state.lookup.insert(new_fd, file); } } JournalEntry::DuplicateFileDescriptorV1 { @@ -189,8 +189,8 @@ impl WritableJournal for MutexState { copied_fd, } => { state.seed.clip_val(copied_fd + 1); - if let Some(file) = state.journal_lookup.get(&original_fd).cloned() { - state.journal_lookup.insert(copied_fd, file); + if let Some(file) = state.lookup.get(&original_fd).cloned() { + state.lookup.insert(copied_fd, file); } } JournalEntry::CreateDirectoryV1 { fd, path } => { @@ -201,7 +201,7 @@ impl WritableJournal for MutexState { } JournalEntry::FileDescriptorSetSizeV1 { fd, st_size } => { let handle = state.handle.clone(); - if let Some(file) = state.journal_lookup.get(&fd) { + if let Some(file) = state.lookup.get(&fd) { handle.block_on(async { let mut file = file.lock().await; file.set_len(st_size) @@ -210,7 +210,7 @@ impl WritableJournal for MutexState { } JournalEntry::FileDescriptorAllocateV1 { fd, offset, len } => { let handle = state.handle.clone(); - if let Some(file) = state.journal_lookup.get(&fd) { + if let Some(file) = state.lookup.get(&fd) { handle.block_on(async { let mut file = file.lock().await; file.set_len(offset + len) @@ -326,33 +326,12 @@ impl Filesystem for JournalFileSystem { } }; - let mut state = self.state.inner.lock().unwrap(); - let file = state - .mem_fs - .new_open_options() - .write(true) - .read(true) - .open(&Path::new(path.as_ref())); - let file = match file { - Ok(a) => a, - Err(FsError::EntryNotFound) => { - tracing::trace!("fs::open new_open_options({}) err=ENOENT", path); - reply.error(libc::ENOENT); - return; - } - Err(err) => { - tracing::trace!("fs::open new_open_options({}) err={}", path, err); - reply.error(libc::EIO); - return; - } + // Reserve a file descriptor + let fh = { + let mut state = self.state.inner.lock().unwrap(); + state.seed.next_val() }; - let fh = state.seed.next_val(); - state - .journal_lookup - .insert(fh, Arc::new(tokio::sync::Mutex::new(file))); - drop(state); - // Write the journals let entry = JournalEntry::OpenFileDescriptorV1 { fd: fh, @@ -364,12 +343,53 @@ impl Filesystem for JournalFileSystem { fs_rights_inheriting: wasi::Rights::all(), fs_flags: wasi::Fdflags::empty(), }; - self.state.write(entry.clone()); - self.journal.write(entry); + if self.state.write(entry.clone()).is_err() { + reply.error(libc::EIO); + return; + } + if self.journal.write(entry).is_err() { + reply.error(libc::EIO); + return; + } reply.opened(fh as u64, flags); } + fn release( + &mut self, + _req: &Request, + _ino: u64, + fh: u64, + _flags: u32, + _lock_owner: u64, + _flush: bool, + reply: ReplyEmpty, + ) { + let fh = fh as u32; + + { + // Check that the file handle exists + let mut state = self.state.inner.lock().unwrap(); + if !state.lookup.contains_key(&fh) { + reply.error(libc::ENOENT); + return; + } + } + + // Write the journals + let entry = JournalEntry::CloseFileDescriptorV1 { fd: fh }; + if self.state.write(entry.clone()).is_err() { + reply.error(libc::EIO); + return; + } + if self.journal.write(entry).is_err() { + reply.error(libc::EIO); + return; + } + + reply.ok(); + } + fn create( &mut self, _req: &Request, @@ -389,36 +409,12 @@ impl Filesystem for JournalFileSystem { path.hash(&mut hasher); let ino = hasher.finish(); - // Now create the new file - let mut state = self.state.inner.lock().unwrap(); - let file = state - .mem_fs - .new_open_options() - .create(true) - .write(true) - .read(true) - .open(&Path::new(path.as_ref())); - let file = match file { - Ok(a) => a, - Err(FsError::EntryNotFound) => { - tracing::trace!("fs::create new_open_options({}) err=ENOENT", path); - reply.error(libc::ENOENT); - return; - } - Err(err) => { - tracing::trace!("fs::create new_open_options({}) err={}", path, err); - reply.error(libc::EIO); - return; - } + // Reserve a file descriptor + let fh = { + let mut state = self.state.inner.lock().unwrap(); + state.seed.next_val() }; - // Create the file and load it into the lookup - let fh = state.seed.next_val(); - state - .journal_lookup - .insert(fh, Arc::new(tokio::sync::Mutex::new(file))); - drop(state); - // Write the journals let entry = JournalEntry::OpenFileDescriptorV1 { fd: fh, @@ -430,8 +426,14 @@ impl Filesystem for JournalFileSystem { fs_rights_inheriting: wasi::Rights::all(), fs_flags: wasi::Fdflags::empty(), }; - self.state.write(entry.clone()); - self.journal.write(entry); + if self.state.write(entry.clone()).is_err() { + reply.error(libc::EIO); + return; + } + if self.journal.write(entry).is_err() { + reply.error(libc::EIO); + return; + } let now = time::get_time(); reply.created( @@ -467,9 +469,11 @@ impl Filesystem for JournalFileSystem { size: u32, reply: ReplyData, ) { + let fh = fh as u32; + // Grab the file from the file handle let mut state = self.state.inner.lock().unwrap(); - let file = match state.fuse_lookup.get_mut(&fh) { + let file = match state.lookup.get_mut(&fh) { Some(a) => a, None => { tracing::trace!("fs::read lookup(fh={fh}) noent err=EIO"); @@ -480,6 +484,8 @@ impl Filesystem for JournalFileSystem { // Read the data from the file and return it let data: Result<_, io::Error> = self.handle.block_on(async { + let mut file = file.lock().await; + let mut buf = Vec::with_capacity(size as usize); unsafe { buf.set_len(size as usize) }; file.seek(io::SeekFrom::Start(offset as u64)).await?; @@ -500,6 +506,46 @@ impl Filesystem for JournalFileSystem { reply.data(&data); } + fn write( + &mut self, + _req: &Request, + _ino: u64, + fh: u64, + offset: i64, + data: &[u8], + _flags: u32, + reply: ReplyWrite, + ) { + let fh = fh as u32; + + { + // Check that the file handle exists + let mut state = self.state.inner.lock().unwrap(); + if !state.lookup.contains_key(&fh) { + reply.error(libc::ENOENT); + return; + } + } + + // Write the entry to the log file + let entry = JournalEntry::FileDescriptorWriteV1 { + fd: fh as u32, + offset: offset as u64, + data: data.into(), + is_64bit: false, + }; + if self.state.write(entry.clone()).is_err() { + reply.error(libc::EIO); + return; + } + if self.journal.write(entry).is_err() { + reply.error(libc::EIO); + return; + } + + reply.written(data.len() as u32); + } + fn readdir( &mut self, _req: &Request, diff --git a/lib/virtual-fs/src/mem_fs/file.rs b/lib/virtual-fs/src/mem_fs/file.rs index 9ac957d4f1e..7ce38aac6b7 100644 --- a/lib/virtual-fs/src/mem_fs/file.rs +++ b/lib/virtual-fs/src/mem_fs/file.rs @@ -1346,6 +1346,8 @@ impl fmt::Debug for FileHandle { formatter .debug_struct("FileHandle") .field("inode", &self.inode) + .field("readable", &self.readable) + .field("writable", &self.writable) .finish() } } From 5f4d2db3aab1aa39411b0c11e53d6d67dc6b74e1 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Tue, 23 Jan 2024 10:30:18 +1100 Subject: [PATCH 18/67] Now using COW on aligned vectors and strings --- lib/journal/src/concrete/aligned_cow_str.rs | 107 ++++++++++ lib/journal/src/concrete/aligned_cow_vec.rs | 210 ++++++++++++++++++++ lib/journal/src/concrete/archived.rs | 170 +++++++--------- lib/journal/src/concrete/mod.rs | 4 + lib/wasix/src/journal/concrete/archived.rs | 2 +- 5 files changed, 395 insertions(+), 98 deletions(-) create mode 100644 lib/journal/src/concrete/aligned_cow_str.rs create mode 100644 lib/journal/src/concrete/aligned_cow_vec.rs diff --git a/lib/journal/src/concrete/aligned_cow_str.rs b/lib/journal/src/concrete/aligned_cow_str.rs new file mode 100644 index 00000000000..e64110ec600 --- /dev/null +++ b/lib/journal/src/concrete/aligned_cow_str.rs @@ -0,0 +1,107 @@ +use std::{ + borrow::{Borrow, BorrowMut, Cow}, + fmt::{self, Pointer}, + ops::{Deref, DerefMut}, +}; + +use rkyv::{ + ser::{ScratchSpace, Serializer}, + vec::{ArchivedVec, VecResolver}, + Archive, Archived, Serialize, +}; + +#[derive(Clone)] +pub struct AlignedCowStr<'a> { + inner: Cow<'a, str>, +} + +impl<'a> AlignedCowStr<'a> { + pub const ALIGNMENT: usize = 16; + + pub fn into_inner(self) -> Cow<'a, str> { + self.inner + } + + #[inline] + pub fn as_slice(&self) -> &str { + self.inner.as_ref() + } + + pub fn len(&self) -> usize { + self.inner.len() + } +} + +impl<'a> Default for AlignedCowStr<'a> { + fn default() -> Self { + Self { + inner: String::new().into(), + } + } +} + +impl<'a> fmt::Debug for AlignedCowStr<'a> { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.fmt(f) + } +} + +impl<'a> From for AlignedCowStr<'a> { + fn from(value: String) -> Self { + Self { + inner: value.into(), + } + } +} + +impl<'a> Into for AlignedCowStr<'a> { + fn into(self) -> String { + self.inner.into_owned() + } +} + +impl<'a> From> for AlignedCowStr<'a> { + fn from(value: Cow<'a, str>) -> Self { + Self { inner: value } + } +} + +impl<'a> Into> for AlignedCowStr<'a> { + fn into(self) -> Cow<'a, str> { + self.inner + } +} + +impl<'a> Deref for AlignedCowStr<'a> { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.inner.deref() + } +} + +impl<'a> AsRef for AlignedCowStr<'a> { + #[inline] + fn as_ref(&self) -> &str { + self.inner.as_ref() + } +} + +impl<'a> Archive for AlignedCowStr<'a> { + type Archived = ArchivedVec; + type Resolver = VecResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedVec::resolve_from_len(self.inner.as_bytes().len(), pos, resolver, out); + } +} + +impl<'a, S: ScratchSpace + Serializer + ?Sized> Serialize for AlignedCowStr<'a> { + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + serializer.align(Self::ALIGNMENT)?; + ArchivedVec::>::serialize_from_slice(self.inner.as_bytes(), serializer) + } +} diff --git a/lib/journal/src/concrete/aligned_cow_vec.rs b/lib/journal/src/concrete/aligned_cow_vec.rs new file mode 100644 index 00000000000..663d99dafec --- /dev/null +++ b/lib/journal/src/concrete/aligned_cow_vec.rs @@ -0,0 +1,210 @@ +use std::{ + borrow::{Borrow, BorrowMut, Cow}, + fmt::{self, Pointer}, + ops::{Deref, DerefMut}, +}; + +use rkyv::{ + ser::{ScratchSpace, Serializer}, + vec::{ArchivedVec, VecResolver}, + Archive, Archived, Serialize, +}; + +/// An aligned COW vector of bytes which avoids copying data +/// when its constructed. The vector is aligned on the 16-byte +/// boundary +#[derive(Clone)] +pub struct AlignedCowVec<'a, T> +where + [T]: ToOwned, +{ + inner: Cow<'a, [T]>, +} + +impl<'a, T> AlignedCowVec<'a, T> +where + T: 'a, + [T]: ToOwned, +{ + /// The alignment of the vector + pub const ALIGNMENT: usize = 16; + + pub fn into_inner(self) -> Cow<'a, [T]> { + self.inner + } + + #[inline] + pub fn as_slice(&self) -> &[T] { + self.inner.as_ref() + } + + pub fn len(&self) -> usize { + self.inner.len() + } + + pub fn len_with_padding(&self) -> usize { + let mut ret = self.inner.len() * std::mem::size_of::(); + let padding = ret % Self::ALIGNMENT; + if padding != 0 { + ret += Self::ALIGNMENT - padding; + } + ret + } +} + +impl<'a, T> Default for AlignedCowVec<'a, T> +where + T: 'a + Clone, + [T]: ToOwned, +{ + fn default() -> Self { + Self { + inner: Vec::new().into(), + } + } +} + +impl<'a, T> fmt::Debug for AlignedCowVec<'a, T> +where + T: 'a, + [T]: ToOwned, +{ + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_slice().fmt(f) + } +} + +impl<'a, T> From> for AlignedCowVec<'a, T> +where + T: 'a + Clone, + [T]: ToOwned, +{ + fn from(value: Vec) -> Self { + Self { + inner: value.into(), + } + } +} + +impl<'a> Into> for AlignedCowVec<'a, u8> { + fn into(self) -> Vec { + self.inner.into_owned() + } +} + +impl<'a, T> From> for AlignedCowVec<'a, T> +where + T: 'a, + [T]: ToOwned, +{ + fn from(value: Cow<'a, [T]>) -> Self { + Self { inner: value } + } +} + +impl<'a, T> Into> for AlignedCowVec<'a, T> +where + T: 'a, + [T]: ToOwned, +{ + fn into(self) -> Cow<'a, [T]> { + self.inner + } +} + +impl<'a, T> Deref for AlignedCowVec<'a, T> +where + T: 'a, + [T]: ToOwned, +{ + type Target = [T]; + + fn deref(&self) -> &Self::Target { + self.inner.deref() + } +} + +impl<'a, T> DerefMut for AlignedCowVec<'a, T> +where + T: 'a, + [T]: ToOwned, + <[T] as ToOwned>::Owned: BorrowMut<[T]>, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + self.inner.to_mut().borrow_mut() + } +} + +impl<'a, T> AsMut<[T]> for AlignedCowVec<'a, T> +where + T: 'a, + [T]: ToOwned, + <[T] as ToOwned>::Owned: BorrowMut<[T]>, +{ + #[inline] + fn as_mut(&mut self) -> &mut [T] { + self.inner.to_mut().borrow_mut() + } +} + +impl<'a, T> AsRef<[T]> for AlignedCowVec<'a, T> +where + T: 'a, + [T]: ToOwned, +{ + #[inline] + fn as_ref(&self) -> &[T] { + self.inner.as_ref() + } +} + +impl<'a, T> Borrow<[T]> for AlignedCowVec<'a, T> +where + T: 'a, + [T]: ToOwned, +{ + #[inline] + fn borrow(&self) -> &[T] { + self.inner.borrow() + } +} + +impl<'a, T> BorrowMut<[T]> for AlignedCowVec<'a, T> +where + T: 'a, + [T]: ToOwned, + <[T] as ToOwned>::Owned: BorrowMut<[T]>, +{ + #[inline] + fn borrow_mut(&mut self) -> &mut [T] { + self.inner.to_mut().borrow_mut() + } +} + +impl<'a, T> Archive for AlignedCowVec<'a, T> +where + T: 'a, + [T]: ToOwned, +{ + type Archived = ArchivedVec; + type Resolver = VecResolver; + + #[inline] + unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) { + ArchivedVec::resolve_from_len(self.len(), pos, resolver, out); + } +} + +impl<'a, T, S: ScratchSpace + Serializer + ?Sized> Serialize for AlignedCowVec<'a, T> +where + T: 'a, + [T]: ToOwned, + T: Serialize, +{ + #[inline] + fn serialize(&self, serializer: &mut S) -> Result { + serializer.align(Self::ALIGNMENT)?; + ArchivedVec::>::serialize_from_slice(self.as_slice(), serializer) + } +} diff --git a/lib/journal/src/concrete/archived.rs b/lib/journal/src/concrete/archived.rs index 95f325b3c2f..9c5ba16a8d1 100644 --- a/lib/journal/src/concrete/archived.rs +++ b/lib/journal/src/concrete/archived.rs @@ -1,9 +1,7 @@ use lz4_flex::block::compress_prepend_size; use num_enum::{IntoPrimitive, TryFromPrimitive}; use rkyv::ser::{ScratchSpace, Serializer}; -use rkyv::{ - AlignedVec, Archive, CheckBytes, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize, -}; +use rkyv::{Archive, CheckBytes, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; use std::time::{Duration, SystemTime}; @@ -439,7 +437,7 @@ impl<'a> JournalEntry<'a> { serializer.serialize_value(&JournalEntryUpdateMemoryRegionV1 { start: region.start, end: region.end, - compressed_data: to_aligned_vec(compress_prepend_size(data.as_ref())), + compressed_data: compress_prepend_size(data.as_ref()).into(), }) } JournalEntry::ProcessExitV1 { exit_code } => { @@ -455,9 +453,9 @@ impl<'a> JournalEntry<'a> { is_64bit, } => serializer.serialize_value(&JournalEntrySetThreadV1 { id, - call_stack: to_aligned_vec(call_stack), - memory_stack: to_aligned_vec(memory_stack), - store_data: to_aligned_vec(store_data), + call_stack: call_stack.into(), + memory_stack: memory_stack.into(), + store_data: store_data.into(), is_64bit, }), JournalEntry::CloseThreadV1 { id, exit_code } => { @@ -480,7 +478,7 @@ impl<'a> JournalEntry<'a> { } => serializer.serialize_value(&JournalEntryFileDescriptorWriteV1 { fd, offset, - data: to_aligned_vec(data), + data: data.into(), is_64bit, }), JournalEntry::SetClockTimeV1 { clock_id, time } => { @@ -505,7 +503,7 @@ impl<'a> JournalEntry<'a> { fd, dirfd, dirflags, - path: to_aligned_vec(path.as_bytes()), + path: path.into(), o_flags: o_flags.bits(), fs_rights_base: fs_rights_base.bits(), fs_rights_inheriting: fs_rights_inheriting.bits(), @@ -524,13 +522,13 @@ impl<'a> JournalEntry<'a> { JournalEntry::CreateDirectoryV1 { fd, path } => { serializer.serialize_value(&JournalEntryCreateDirectoryV1 { fd, - path: to_aligned_vec(path.as_bytes()), + path: path.into(), }) } JournalEntry::RemoveDirectoryV1 { fd, path } => { serializer.serialize_value(&JournalEntryRemoveDirectoryV1 { fd, - path: to_aligned_vec(path.as_bytes()), + path: path.into(), }) } JournalEntry::PathSetTimesV1 { @@ -543,7 +541,7 @@ impl<'a> JournalEntry<'a> { } => serializer.serialize_value(&JournalEntryPathSetTimesV1 { fd, flags, - path: to_aligned_vec(path.as_bytes()), + path: path.into(), st_atim, st_mtim, fst_flags: fst_flags.bits(), @@ -598,24 +596,24 @@ impl<'a> JournalEntry<'a> { new_path, } => serializer.serialize_value(&JournalEntryCreateHardLinkV1 { old_fd, - old_path: to_aligned_vec(old_path.as_bytes()), + old_path: old_path.into(), old_flags, new_fd, - new_path: to_aligned_vec(new_path.as_bytes()), + new_path: new_path.into(), }), JournalEntry::CreateSymbolicLinkV1 { old_path, fd, new_path, } => serializer.serialize_value(&JournalEntryCreateSymbolicLinkV1 { - old_path: to_aligned_vec(old_path.as_bytes()), + old_path: old_path.into(), fd, - new_path: to_aligned_vec(new_path.as_bytes()), + new_path: new_path.into(), }), JournalEntry::UnlinkFileV1 { fd, path } => { serializer.serialize_value(&JournalEntryUnlinkFileV1 { fd, - path: to_aligned_vec(path.as_bytes()), + path: path.into(), }) } JournalEntry::PathRenameV1 { @@ -625,14 +623,12 @@ impl<'a> JournalEntry<'a> { new_path, } => serializer.serialize_value(&JournalEntryPathRenameV1 { old_fd, - old_path: to_aligned_vec_str(old_path), + old_path: old_path.into(), new_fd, - new_path: to_aligned_vec_str(new_path), + new_path: new_path.into(), }), JournalEntry::ChangeDirectoryV1 { path } => { - serializer.serialize_value(&JournalEntryChangeDirectoryV1 { - path: to_aligned_vec_str(path), - }) + serializer.serialize_value(&JournalEntryChangeDirectoryV1 { path: path.into() }) } JournalEntry::EpollCreateV1 { fd } => { serializer.serialize_value(&JournalEntryEpollCreateV1 { fd }) @@ -686,8 +682,8 @@ impl<'a> JournalEntry<'a> { token, security, } => serializer.serialize_value(&JournalEntryPortBridgeV1 { - network: to_aligned_vec_str(network), - token: to_aligned_vec_str(token), + network: network.into(), + token: token.into(), security: security.into(), }), JournalEntry::PortUnbridgeV1 => serializer.serialize_value(&()), @@ -795,7 +791,7 @@ impl<'a> JournalEntry<'a> { is_64bit, } => serializer.serialize_value(&JournalEntrySocketSendToV1 { fd, - data: to_aligned_vec(data), + data: data.into(), flags, addr, is_64bit, @@ -807,7 +803,7 @@ impl<'a> JournalEntry<'a> { is_64bit, } => serializer.serialize_value(&JournalEntrySocketSendV1 { fd, - data: to_aligned_vec(data), + data: data.into(), flags, is_64bit, }), @@ -852,26 +848,6 @@ impl<'a> JournalEntry<'a> { } } -fn to_aligned_vec(data: D) -> AlignedVec -where - D: AsRef<[u8]>, -{ - let data = data.as_ref(); - let mut ret = AlignedVec::with_capacity(data.len()); - ret.extend_from_slice(&data); - ret -} - -fn to_aligned_vec_str(data: S) -> AlignedVec -where - S: AsRef, -{ - let data = data.as_ref().as_bytes(); - let mut ret = AlignedVec::with_capacity(data.len()); - ret.extend_from_slice(&data); - ret -} - /// The journal log entries are serializable which /// allows them to be written directly to a file /// @@ -888,30 +864,30 @@ pub(crate) struct JournalEntryHeader { pub enum ArchivedJournalEntry<'a> { InitModuleV1(&'a ArchivedJournalEntryInitModuleV1), ProcessExitV1(&'a ArchivedJournalEntryProcessExitV1), - SetThreadV1(&'a ArchivedJournalEntrySetThreadV1), + SetThreadV1(&'a ArchivedJournalEntrySetThreadV1<'a>), CloseThreadV1(&'a ArchivedJournalEntryCloseThreadV1), FileDescriptorSeekV1(&'a ArchivedJournalEntryFileDescriptorSeekV1), - FileDescriptorWriteV1(&'a ArchivedJournalEntryFileDescriptorWriteV1), - UpdateMemoryRegionV1(&'a ArchivedJournalEntryUpdateMemoryRegionV1), + FileDescriptorWriteV1(&'a ArchivedJournalEntryFileDescriptorWriteV1<'a>), + UpdateMemoryRegionV1(&'a ArchivedJournalEntryUpdateMemoryRegionV1<'a>), SetClockTimeV1(&'a ArchivedJournalEntrySetClockTimeV1), - OpenFileDescriptorV1(&'a ArchivedJournalEntryOpenFileDescriptorV1), + OpenFileDescriptorV1(&'a ArchivedJournalEntryOpenFileDescriptorV1<'a>), CloseFileDescriptorV1(&'a ArchivedJournalEntryCloseFileDescriptorV1), RenumberFileDescriptorV1(&'a ArchivedJournalEntryRenumberFileDescriptorV1), DuplicateFileDescriptorV1(&'a ArchivedJournalEntryDuplicateFileDescriptorV1), - CreateDirectoryV1(&'a ArchivedJournalEntryCreateDirectoryV1), - RemoveDirectoryV1(&'a ArchivedJournalEntryRemoveDirectoryV1), - PathSetTimesV1(&'a ArchivedJournalEntryPathSetTimesV1), + CreateDirectoryV1(&'a ArchivedJournalEntryCreateDirectoryV1<'a>), + RemoveDirectoryV1(&'a ArchivedJournalEntryRemoveDirectoryV1<'a>), + PathSetTimesV1(&'a ArchivedJournalEntryPathSetTimesV1<'a>), FileDescriptorSetTimesV1(&'a ArchivedJournalEntryFileDescriptorSetTimesV1), FileDescriptorSetSizeV1(&'a ArchivedJournalEntryFileDescriptorSetSizeV1), FileDescriptorSetFlagsV1(&'a ArchivedJournalEntryFileDescriptorSetFlagsV1), FileDescriptorSetRightsV1(&'a ArchivedJournalEntryFileDescriptorSetRightsV1), FileDescriptorAdviseV1(&'a ArchivedJournalEntryFileDescriptorAdviseV1), FileDescriptorAllocateV1(&'a ArchivedJournalEntryFileDescriptorAllocateV1), - CreateHardLinkV1(&'a ArchivedJournalEntryCreateHardLinkV1), - CreateSymbolicLinkV1(&'a ArchivedJournalEntryCreateSymbolicLinkV1), - UnlinkFileV1(&'a ArchivedJournalEntryUnlinkFileV1), - PathRenameV1(&'a ArchivedJournalEntryPathRenameV1), - ChangeDirectoryV1(&'a ArchivedJournalEntryChangeDirectoryV1), + CreateHardLinkV1(&'a ArchivedJournalEntryCreateHardLinkV1<'a>), + CreateSymbolicLinkV1(&'a ArchivedJournalEntryCreateSymbolicLinkV1<'a>), + UnlinkFileV1(&'a ArchivedJournalEntryUnlinkFileV1<'a>), + PathRenameV1(&'a ArchivedJournalEntryPathRenameV1<'a>), + ChangeDirectoryV1(&'a ArchivedJournalEntryChangeDirectoryV1<'a>), EpollCreateV1(&'a ArchivedJournalEntryEpollCreateV1), EpollCtlV1(&'a ArchivedJournalEntryEpollCtlV1), TtySetV1(&'a ArchivedJournalEntryTtySetV1), @@ -920,7 +896,7 @@ pub enum ArchivedJournalEntry<'a> { PortAddAddrV1(&'a ArchivedJournalEntryPortAddAddrV1), PortDelAddrV1(&'a ArchivedJournalEntryPortDelAddrV1), PortAddrClearV1, - PortBridgeV1(&'a ArchivedJournalEntryPortBridgeV1), + PortBridgeV1(&'a ArchivedJournalEntryPortBridgeV1<'a>), PortUnbridgeV1, PortDhcpAcquireV1, PortGatewaySetV1(&'a ArchivedJournalEntryPortGatewaySetV1), @@ -937,8 +913,8 @@ pub enum ArchivedJournalEntry<'a> { SocketLeaveIpv4MulticastV1(&'a ArchivedJournalEntrySocketLeaveIpv4MulticastV1), SocketLeaveIpv6MulticastV1(&'a ArchivedJournalEntrySocketLeaveIpv6MulticastV1), SocketSendFileV1(&'a ArchivedJournalEntrySocketSendFileV1), - SocketSendToV1(&'a ArchivedJournalEntrySocketSendToV1), - SocketSendV1(&'a ArchivedJournalEntrySocketSendV1), + SocketSendToV1(&'a ArchivedJournalEntrySocketSendToV1<'a>), + SocketSendV1(&'a ArchivedJournalEntrySocketSendV1<'a>), SocketSetOptFlagV1(&'a ArchivedJournalEntrySocketSetOptFlagV1), SocketSetOptSizeV1(&'a ArchivedJournalEntrySocketSetOptSizeV1), SocketSetOptTimeV1(&'a ArchivedJournalEntrySocketSetOptTimeV1), @@ -966,11 +942,11 @@ pub struct JournalEntryProcessExitV1 { #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes), repr(align(8)))] -pub struct JournalEntrySetThreadV1 { +pub struct JournalEntrySetThreadV1<'a> { pub id: u32, - pub call_stack: AlignedVec, - pub memory_stack: AlignedVec, - pub store_data: AlignedVec, + pub call_stack: AlignedCowVec<'a, u8>, + pub memory_stack: AlignedCowVec<'a, u8>, + pub store_data: AlignedCowVec<'a, u8>, pub is_64bit: bool, } @@ -997,8 +973,8 @@ pub struct JournalEntryFileDescriptorSeekV1 { #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes), repr(align(8)))] -pub struct JournalEntryFileDescriptorWriteV1 { - pub data: AlignedVec, +pub struct JournalEntryFileDescriptorWriteV1<'a> { + pub data: AlignedCowVec<'a, u8>, pub offset: u64, pub fd: u32, pub is_64bit: bool, @@ -1008,8 +984,8 @@ pub struct JournalEntryFileDescriptorWriteV1 { #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes), repr(align(8)))] -pub struct JournalEntryUpdateMemoryRegionV1 { - pub compressed_data: AlignedVec, +pub struct JournalEntryUpdateMemoryRegionV1<'a> { + pub compressed_data: AlignedCowVec<'a, u8>, pub start: u64, pub end: u64, } @@ -1027,7 +1003,7 @@ pub struct JournalEntrySetClockTimeV1 { #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes), repr(align(8)))] -pub struct JournalEntryOpenFileDescriptorV1 { +pub struct JournalEntryOpenFileDescriptorV1<'a> { pub fd: u32, pub dirfd: u32, pub dirflags: u32, @@ -1035,7 +1011,7 @@ pub struct JournalEntryOpenFileDescriptorV1 { pub o_flags: u16, pub fs_rights_base: u64, pub fs_rights_inheriting: u64, - pub path: AlignedVec, + pub path: AlignedCowStr<'a>, } #[repr(C)] @@ -1068,28 +1044,28 @@ pub struct JournalEntryDuplicateFileDescriptorV1 { #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes), repr(align(8)))] -pub struct JournalEntryCreateDirectoryV1 { +pub struct JournalEntryCreateDirectoryV1<'a> { pub fd: u32, - pub path: AlignedVec, + pub path: AlignedCowStr<'a>, } #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes), repr(align(8)))] -pub struct JournalEntryRemoveDirectoryV1 { +pub struct JournalEntryRemoveDirectoryV1<'a> { pub fd: u32, - pub path: AlignedVec, + pub path: AlignedCowStr<'a>, } #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes), repr(align(8)))] -pub struct JournalEntryPathSetTimesV1 { +pub struct JournalEntryPathSetTimesV1<'a> { pub fd: u32, pub flags: u32, - pub path: AlignedVec, + pub path: AlignedCowStr<'a>, pub st_atim: u64, pub st_mtim: u64, pub fst_flags: u16, @@ -1159,50 +1135,50 @@ pub struct JournalEntryFileDescriptorAllocateV1 { #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes), repr(align(8)))] -pub struct JournalEntryCreateHardLinkV1 { +pub struct JournalEntryCreateHardLinkV1<'a> { pub old_fd: u32, - pub old_path: AlignedVec, + pub old_path: AlignedCowStr<'a>, pub old_flags: u32, pub new_fd: u32, - pub new_path: AlignedVec, + pub new_path: AlignedCowStr<'a>, } #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes), repr(align(8)))] -pub struct JournalEntryCreateSymbolicLinkV1 { +pub struct JournalEntryCreateSymbolicLinkV1<'a> { pub fd: u32, - pub old_path: AlignedVec, - pub new_path: AlignedVec, + pub old_path: AlignedCowStr<'a>, + pub new_path: AlignedCowStr<'a>, } #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes), repr(align(8)))] -pub struct JournalEntryUnlinkFileV1 { +pub struct JournalEntryUnlinkFileV1<'a> { pub fd: u32, - pub path: AlignedVec, + pub path: AlignedCowStr<'a>, } #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes), repr(align(8)))] -pub struct JournalEntryPathRenameV1 { +pub struct JournalEntryPathRenameV1<'a> { pub old_fd: u32, - pub old_path: AlignedVec, + pub old_path: AlignedCowStr<'a>, pub new_fd: u32, - pub new_path: AlignedVec, + pub new_path: AlignedCowStr<'a>, } #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes), repr(align(8)))] -pub struct JournalEntryChangeDirectoryV1 { - pub path: AlignedVec, +pub struct JournalEntryChangeDirectoryV1<'a> { + pub path: AlignedCowStr<'a>, } #[repr(C)] @@ -1280,9 +1256,9 @@ pub struct JournalEntryPortDelAddrV1 { #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes), repr(align(8)))] -pub struct JournalEntryPortBridgeV1 { - pub network: AlignedVec, - pub token: AlignedVec, +pub struct JournalEntryPortBridgeV1<'a> { + pub network: AlignedCowStr<'a>, + pub token: AlignedCowStr<'a>, pub security: JournalStreamSecurityV1, } @@ -1418,9 +1394,9 @@ pub struct JournalEntrySocketSendFileV1 { #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes), repr(align(8)))] -pub struct JournalEntrySocketSendToV1 { +pub struct JournalEntrySocketSendToV1<'a> { pub fd: u32, - pub data: AlignedVec, + pub data: AlignedCowVec<'a, u8>, pub flags: u16, pub addr: SocketAddr, pub is_64bit: bool, @@ -1430,9 +1406,9 @@ pub struct JournalEntrySocketSendToV1 { #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes), repr(align(8)))] -pub struct JournalEntrySocketSendV1 { +pub struct JournalEntrySocketSendV1<'a> { pub fd: u32, - pub data: AlignedVec, + pub data: AlignedCowVec<'a, u8>, pub flags: u16, pub is_64bit: bool, } diff --git a/lib/journal/src/concrete/mod.rs b/lib/journal/src/concrete/mod.rs index 9cc1abe2224..b564fa1fe81 100644 --- a/lib/journal/src/concrete/mod.rs +++ b/lib/journal/src/concrete/mod.rs @@ -1,3 +1,5 @@ +mod aligned_cow_str; +mod aligned_cow_vec; mod arc; mod archived; mod archived_from; @@ -20,6 +22,8 @@ mod unsupported; pub(super) use super::*; +pub use aligned_cow_str::*; +pub use aligned_cow_vec::*; pub use arc::*; pub use archived::*; pub use boxed::*; diff --git a/lib/wasix/src/journal/concrete/archived.rs b/lib/wasix/src/journal/concrete/archived.rs index 9546cee699e..e7a822a6742 100644 --- a/lib/wasix/src/journal/concrete/archived.rs +++ b/lib/wasix/src/journal/concrete/archived.rs @@ -882,7 +882,7 @@ pub enum ArchivedJournalEntry<'a> { SetThreadV1(&'a ArchivedJournalEntrySetThreadV1), CloseThreadV1(&'a ArchivedJournalEntryCloseThreadV1), FileDescriptorSeekV1(&'a ArchivedJournalEntryFileDescriptorSeekV1), - FileDescriptorWriteV1(&'a ArchivedJournalEntryFileDescriptorWriteV1), + FileDescriptorWriteV1(&'a ArchivedJournalEntryFileDescriptorWriteV1<'a>), UpdateMemoryRegionV1(&'a ArchivedJournalEntryUpdateMemoryRegionV1), SetClockTimeV1(&'a ArchivedJournalEntrySetClockTimeV1), OpenFileDescriptorV1(&'a ArchivedJournalEntryOpenFileDescriptorV1), From 173b07fb3bc4fbb5e58f2b0e9da8f755613ca077 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Tue, 23 Jan 2024 12:56:53 +1100 Subject: [PATCH 19/67] Significant improvement in write performance --- lib/journal/src/concrete/aligned_cow_str.rs | 15 ++++++++------- lib/journal/src/concrete/aligned_cow_vec.rs | 11 ++++------- lib/journal/src/concrete/log_file.rs | 11 ++--------- 3 files changed, 14 insertions(+), 23 deletions(-) diff --git a/lib/journal/src/concrete/aligned_cow_str.rs b/lib/journal/src/concrete/aligned_cow_str.rs index e64110ec600..36a2d7bdef8 100644 --- a/lib/journal/src/concrete/aligned_cow_str.rs +++ b/lib/journal/src/concrete/aligned_cow_str.rs @@ -1,8 +1,4 @@ -use std::{ - borrow::{Borrow, BorrowMut, Cow}, - fmt::{self, Pointer}, - ops::{Deref, DerefMut}, -}; +use std::{borrow::Cow, fmt, ops::Deref}; use rkyv::{ ser::{ScratchSpace, Serializer}, @@ -43,7 +39,7 @@ impl<'a> Default for AlignedCowStr<'a> { impl<'a> fmt::Debug for AlignedCowStr<'a> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.fmt(f) + self.inner.fmt(f) } } @@ -102,6 +98,11 @@ impl<'a, S: ScratchSpace + Serializer + ?Sized> Serialize for AlignedCowStr<' #[inline] fn serialize(&self, serializer: &mut S) -> Result { serializer.align(Self::ALIGNMENT)?; - ArchivedVec::>::serialize_from_slice(self.inner.as_bytes(), serializer) + unsafe { + ArchivedVec::>::serialize_copy_from_slice( + self.inner.as_bytes(), + serializer, + ) + } } } diff --git a/lib/journal/src/concrete/aligned_cow_vec.rs b/lib/journal/src/concrete/aligned_cow_vec.rs index 663d99dafec..eebfde6e7e6 100644 --- a/lib/journal/src/concrete/aligned_cow_vec.rs +++ b/lib/journal/src/concrete/aligned_cow_vec.rs @@ -196,15 +196,12 @@ where } } -impl<'a, T, S: ScratchSpace + Serializer + ?Sized> Serialize for AlignedCowVec<'a, T> -where - T: 'a, - [T]: ToOwned, - T: Serialize, -{ +impl<'a, S: ScratchSpace + Serializer + ?Sized> Serialize for AlignedCowVec<'a, u8> { #[inline] fn serialize(&self, serializer: &mut S) -> Result { serializer.align(Self::ALIGNMENT)?; - ArchivedVec::>::serialize_from_slice(self.as_slice(), serializer) + unsafe { + ArchivedVec::>::serialize_copy_from_slice(self.as_slice(), serializer) + } } } diff --git a/lib/journal/src/concrete/log_file.rs b/lib/journal/src/concrete/log_file.rs index cc04b59746d..767fb84db21 100644 --- a/lib/journal/src/concrete/log_file.rs +++ b/lib/journal/src/concrete/log_file.rs @@ -130,18 +130,11 @@ impl WritableJournal for LogFileJournalTx { // Write the header (with a record size of zero) let record_type: JournalEntryRecordType = entry.archive_record_type(); - let offset_header = state.file.stream_position()?; + let offset_header = state.serializer.pos() as u64; state.serializer.write(&[0u8; 8])?; - // Now serialize the actual data to the log (we need to pad to a - // location that is aligned with the structure) + // Now serialize the actual data to the log let offset_start = state.serializer.pos() as u64; - if state.serializer.pos() as u64 != offset_start { - return Err(anyhow::format_err!( - "serializer has misaligned positioning ({} vs {offset_start})", - state.serializer.pos() - )); - } entry.serialize_archive(&mut state.serializer)?; let offset_end = state.serializer.pos() as u64; let record_size = offset_end - offset_start; From e12e14616aac5e2d087278594960d760d94a1c0a Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Wed, 24 Jan 2024 02:00:48 +1100 Subject: [PATCH 20/67] Finished a progress bar for the loading of a journal --- Cargo.lock | 2 + Cargo.toml | 2 + lib/cli/Cargo.toml | 6 +- lib/cli/src/commands/journal/mount/cmd.rs | 9 +- lib/cli/src/commands/journal/mount/fs.rs | 250 ++++++++++++++++-- lib/compiler/Cargo.toml | 2 +- lib/journal/Cargo.toml | 2 +- lib/journal/src/concrete/arc.rs | 8 +- lib/journal/src/concrete/boxed.rs | 8 +- lib/journal/src/concrete/buffered.rs | 18 +- lib/journal/src/concrete/compacting.rs | 16 +- .../src/concrete/compacting_log_file.rs | 16 +- lib/journal/src/concrete/counting.rs | 9 +- lib/journal/src/concrete/filter.rs | 53 +++- lib/journal/src/concrete/log_file.rs | 62 +++-- lib/journal/src/concrete/null.rs | 9 +- lib/journal/src/concrete/pipe.rs | 52 ++-- lib/journal/src/concrete/printing.rs | 9 +- lib/journal/src/concrete/recombined.rs | 4 +- lib/journal/src/concrete/unsupported.rs | 4 +- lib/journal/src/lib.rs | 38 ++- lib/journal/src/util.rs | 2 +- lib/types/Cargo.toml | 2 +- lib/virtual-fs/src/lib.rs | 6 + lib/virtual-fs/src/mem_fs/file.rs | 39 ++- lib/virtual-fs/src/mem_fs/file_opener.rs | 2 +- lib/virtual-fs/src/mem_fs/filesystem.rs | 11 +- lib/virtual-fs/src/mem_fs/mod.rs | 1 + lib/virtual-fs/src/mem_fs/offloaded_file.rs | 163 ++++++++++-- lib/virtual-net/Cargo.toml | 2 +- lib/wasix/Cargo.toml | 4 +- lib/wasix/src/syscalls/journal.rs | 2 +- 32 files changed, 641 insertions(+), 172 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a33c75a8c2..f95054cc2f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5997,10 +5997,12 @@ dependencies = [ "prettytable-rs", "regex", "reqwest", + "rkyv", "semver 1.0.20", "serde", "serde_json", "sha2", + "shared-buffer", "spinoff", "tar", "target-lexicon 0.12.12", diff --git a/Cargo.toml b/Cargo.toml index 3019bed58a7..005250e8aa7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,6 +89,8 @@ memoffset = "0.9.0" wasmer-toml = "0.9.2" webc = { version = "5.8.0", default-features = false, features = ["package"] } shared-buffer = "0.1.4" +rkyv = { version = "0.7.40", features = ["indexmap", "validation", "strict"] } +memmap2 = { version = "0.6.2" } [build-dependencies] test-generator = { path = "tests/lib/test-generator" } diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index bee808e02a4..e253da1a40e 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -69,6 +69,8 @@ webc = { workspace = true } wasmer-edge-cli = { version = "=0.1.4", default-features = false } # Used by the mount command +shared-buffer = { workspace = true, optional = true } +rkyv = { workspace = true, optional = true } fuse = { version = "0.3", optional = true } time = { version = "0.1.45", optional = true } @@ -115,7 +117,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } async-trait = "0.1.68" tokio = { version = "1.28.1", features = ["macros", "rt-multi-thread"] } once_cell = "1.17.1" -indicatif = "0.17.5" +indicatif = "0.17.7" opener = "0.6.1" hyper = { version = "0.14.27", features = ["server"] } http = "0.2.9" @@ -174,7 +176,7 @@ default = [ "static-artifact-create", ] journal = ["wasmer-wasix/journal"] -fuse = ["dep:fuse", "dep:time"] +fuse = ["dep:fuse", "dep:time", "dep:shared-buffer", "dep:rkyv"] backend = [] coredump = ["wasm-coredump-builder"] sys = ["compiler", "wasmer-vm"] diff --git a/lib/cli/src/commands/journal/mount/cmd.rs b/lib/cli/src/commands/journal/mount/cmd.rs index f19e4ebf322..85da3b1289a 100644 --- a/lib/cli/src/commands/journal/mount/cmd.rs +++ b/lib/cli/src/commands/journal/mount/cmd.rs @@ -4,7 +4,7 @@ use clap::Parser; use wasmer_edge_cli::cmd::AsyncCliCommand; use wasmer_wasix::fs::WasiFdSeed; -use super::fs::JournalFileSystem; +use super::fs::JournalFileSystemBuilder; /// Mounts a journal as a file system on the local machine #[derive(Debug, Parser)] @@ -25,8 +25,11 @@ impl AsyncCliCommand for CmdJournalMount { impl CmdJournalMount { async fn run(self) -> Result<(), anyhow::Error> { - let fs: JournalFileSystem = - JournalFileSystem::new(&self.journal_path, WasiFdSeed::default())?; + let fs = JournalFileSystemBuilder::new(&self.journal_path) + .with_fd_seed(WasiFdSeed::default()) + .with_progress_bar(true) + .build()?; + tokio::task::spawn_blocking(move || { // First we unmount any existing file system on this path std::process::Command::new("/bin/umount") diff --git a/lib/cli/src/commands/journal/mount/fs.rs b/lib/cli/src/commands/journal/mount/fs.rs index 20efe656411..45892998b67 100644 --- a/lib/cli/src/commands/journal/mount/fs.rs +++ b/lib/cli/src/commands/journal/mount/fs.rs @@ -3,9 +3,10 @@ use std::{ borrow::Cow, collections::HashMap, ffi::OsStr, + fs::File, hash::{Hash, Hasher}, io, - path::Path, + path::{Path, PathBuf}, sync::{atomic::AtomicU32, Arc, Mutex}, time::Duration, }; @@ -14,12 +15,19 @@ use fuse::{ FileAttr, Filesystem, ReplyAttr, ReplyCreate, ReplyData, ReplyDirectory, ReplyEmpty, ReplyEntry, ReplyOpen, ReplyWrite, Request, }; +use indicatif::{ProgressBar, ProgressStyle}; +use shared_buffer::OwnedBuffer; use tokio::runtime::Handle; -use virtual_fs::{mem_fs, AsyncReadExt, AsyncSeekExt, AsyncWriteExt, FileSystem, FsError}; +use virtual_fs::{ + mem_fs::{self, OffloadBackingStore}, + AsyncReadExt, AsyncSeekExt, AsyncWriteExt, FileSystem, FsError, +}; use wasmer_wasix::{ fs::WasiFdSeed, journal::{ - copy_journal, Journal, JournalEntry, LogFileJournal, ReadableJournal, WritableJournal, + copy_journal, ArchivedJournalEntry, ArchivedJournalEntryFileDescriptorWriteV1, Journal, + JournalEntry, JournalEntryFileDescriptorWriteV1, LogFileJournal, LogWriteResult, + ReadableJournal, WritableJournal, }, types::Oflags, wasmer_wasix_types::wasi, @@ -36,6 +44,7 @@ struct State { Arc>>, >, seed: WasiFdSeed, + fake_offset: u64, } #[derive(Debug)] @@ -44,31 +53,73 @@ struct MutexState { } #[derive(Debug)] -pub struct JournalFileSystem { - handle: tokio::runtime::Handle, - journal: LogFileJournal, - state: MutexState, +pub struct JournalFileSystemBuilder { + path: PathBuf, + fd_seed: WasiFdSeed, + progress_bar: bool, } -impl JournalFileSystem { +impl JournalFileSystemBuilder { + pub fn new(path: &Path) -> Self { + Self { + path: path.to_path_buf(), + fd_seed: WasiFdSeed::default(), + progress_bar: false, + } + } + + pub fn with_fd_seed(mut self, fd_seed: WasiFdSeed) -> Self { + self.fd_seed = fd_seed; + self + } + + pub fn with_progress_bar(mut self, val: bool) -> Self { + self.progress_bar = val; + self + } + // Opens the journal and copies all its contents into // and memory file system - pub fn new(journal_path: &Path, fd_seed: WasiFdSeed) -> anyhow::Result { - let journal = LogFileJournal::new(journal_path)?; + pub fn build(self) -> anyhow::Result { + let journal = LogFileJournal::new(&self.path)?; - let mem_fs = mem_fs::FileSystem::default(); + let file = File::open(&self.path)?; + let mem_fs = mem_fs::FileSystem::default() + .with_backing_offload(OffloadBackingStore::from_file(&file))?; let state = MutexState { inner: Mutex::new(State { handle: tokio::runtime::Handle::current(), mem_fs, inos: Default::default(), - seed: fd_seed, + seed: self.fd_seed, lookup: Default::default(), + fake_offset: 0, }), }; - tokio::task::block_in_place(|| copy_journal(&journal, &state))?; - let ret = Self { + let progress = if self.progress_bar { + let file_len = file.metadata()?.len(); + + let mut pb = ProgressBar::new(file_len); + pb.set_style(ProgressStyle::with_template("{msg}\n{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})") + .unwrap() + .progress_chars("#>-")); + pb.set_message("Loading journal..."); + + Some(pb) + } else { + None + }; + + tokio::task::block_in_place(|| { + if let Some(progress) = progress { + copy_journal_with_progress(&journal, &state, progress) + } else { + copy_journal(&journal, &state) + } + })?; + + let ret = JournalFileSystem { handle: tokio::runtime::Handle::current(), journal, state, @@ -76,7 +127,29 @@ impl JournalFileSystem { Ok(ret) } +} +pub fn copy_journal_with_progress( + from: &R, + to: &W, + mut progress: ProgressBar, +) -> anyhow::Result<()> { + while let Some(record) = from.read()? { + progress.set_position(record.record_offset); + to.write(record.into_inner())?; + } + progress.finish_with_message("Journal is Mounted"); + Ok(()) +} + +#[derive(Debug)] +pub struct JournalFileSystem { + handle: tokio::runtime::Handle, + journal: LogFileJournal, + state: MutexState, +} + +impl JournalFileSystem { fn reverse_ino(&self, ino: u64) -> Result, libc::c_int> { if ino == 1 { return Ok("/".into()); @@ -133,9 +206,13 @@ impl JournalFileSystem { } impl WritableJournal for MutexState { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { - let ret = entry.estimate_size() as u64; + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { let mut state = self.inner.lock().unwrap(); + let ret = LogWriteResult { + record_offset: state.fake_offset, + record_size: entry.estimate_size() as u64, + }; + state.fake_offset += ret.record_size; match entry { JournalEntry::FileDescriptorWriteV1 { fd, @@ -291,12 +368,18 @@ impl Filesystem for JournalFileSystem { fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { let path = match self.compute_path(parent, name) { Ok(a) => a, - Err(err) => return reply.error(err), + Err(err) => { + tracing::trace!("fs::lookup err={err}"); + return reply.error(err); + } }; match self.attr(path) { Ok(meta) => reply.entry(&time::Timespec::new(1, 0), &meta, 0), - Err(err) => reply.error(err), + Err(err) => { + tracing::trace!("fs::lookup err={err}"); + reply.error(err) + } } } @@ -316,6 +399,73 @@ impl Filesystem for JournalFileSystem { } } + fn setattr( + &mut self, + _req: &Request, + ino: u64, + _mode: Option, + _uid: Option, + _gid: Option, + size: Option, + _atime: Option, + _mtime: Option, + fh: Option, + _crtime: Option, + _chgtime: Option, + _bkuptime: Option, + _flags: Option, + reply: ReplyAttr, + ) { + let file = match fh { + Some(fd) => { + let fd = fd as u32; + let mut state = self.state.inner.lock().unwrap(); + match state.lookup.get_mut(&fd) { + Some(f) => f.clone(), + None => { + tracing::trace!("fs::getattr noent (fd={fd})"); + reply.error(libc::ENOENT); + return; + } + } + } + None => { + tracing::trace!("fs::getattr ino only is not supported"); + reply.error(libc::ENOSYS); + return; + } + }; + + // Read the data from the file and return it + let attr = self.handle.block_on(async { + let mut file = file.lock().await; + + if let Some(new_size) = size { + file.set_len(new_size); + } + + FileAttr { + ino, + size: file.size(), + blocks: (1u64.max(file.size()) - 1 / 512) + 1, + atime: time::Timespec::new(file.last_accessed() as i64, 0), + mtime: time::Timespec::new(file.last_modified() as i64, 0), + ctime: time::Timespec::new(file.created_time() as i64, 0), + crtime: time::Timespec::new(file.created_time() as i64, 0), + kind: fuse::FileType::RegularFile, + perm: 0o644, + nlink: 1, + uid: 0, + gid: 0, + rdev: 0, + flags: 0, + } + }); + + // Return the data + reply.attr(&time::Timespec::new(1, 0), &attr) + } + fn open(&mut self, _req: &Request, ino: u64, flags: u32, reply: ReplyOpen) { let path = match self.reverse_ino(ino) { Ok(a) => a, @@ -344,10 +494,12 @@ impl Filesystem for JournalFileSystem { fs_flags: wasi::Fdflags::empty(), }; if self.state.write(entry.clone()).is_err() { + tracing::trace!("fs::open err=EIO"); reply.error(libc::EIO); return; } if self.journal.write(entry).is_err() { + tracing::trace!("fs::open err=EIO"); reply.error(libc::EIO); return; } @@ -371,6 +523,7 @@ impl Filesystem for JournalFileSystem { // Check that the file handle exists let mut state = self.state.inner.lock().unwrap(); if !state.lookup.contains_key(&fh) { + tracing::trace!("fs::release err=ENOENT (fd={fh})"); reply.error(libc::ENOENT); return; } @@ -379,10 +532,12 @@ impl Filesystem for JournalFileSystem { // Write the journals let entry = JournalEntry::CloseFileDescriptorV1 { fd: fh }; if self.state.write(entry.clone()).is_err() { + tracing::trace!("fs::release err=EIO"); reply.error(libc::EIO); return; } if self.journal.write(entry).is_err() { + tracing::trace!("fs::release err=EIO"); reply.error(libc::EIO); return; } @@ -427,10 +582,12 @@ impl Filesystem for JournalFileSystem { fs_flags: wasi::Fdflags::empty(), }; if self.state.write(entry.clone()).is_err() { + tracing::trace!("fs::create err=EIO"); reply.error(libc::EIO); return; } if self.journal.write(entry).is_err() { + tracing::trace!("fs::create err=EIO"); reply.error(libc::EIO); return; } @@ -522,25 +679,66 @@ impl Filesystem for JournalFileSystem { // Check that the file handle exists let mut state = self.state.inner.lock().unwrap(); if !state.lookup.contains_key(&fh) { + tracing::trace!("fs::write err=ENOENT"); reply.error(libc::ENOENT); return; } } // Write the entry to the log file + let fd = fh as u32; let entry = JournalEntry::FileDescriptorWriteV1 { - fd: fh as u32, + fd, offset: offset as u64, data: data.into(), is_64bit: false, }; - if self.state.write(entry.clone()).is_err() { - reply.error(libc::EIO); - return; - } - if self.journal.write(entry).is_err() { - reply.error(libc::EIO); - return; + + let res = match self.journal.write(entry) { + Ok(res) => res, + Err(_) => { + tracing::trace!("fs::write err=EIO"); + reply.error(libc::EIO); + return; + } + }; + + // We load the record from the journal and use this to write to the memory file system + // because the memory file system has an optimization where it will automatically offload + // the data to the mmap of the journal rather than store it in memory. In effect it offloads + // to the disk + { + let mut state = self.state.inner.lock().unwrap(); + let handle = state.handle.clone(); + if let Some(file) = state.lookup.get_mut(&fd) { + let res: Result<_, io::Error> = handle.block_on(async { + let mut file = file.lock().await; + file.seek(io::SeekFrom::Start(offset as u64)).await; + + // make sure adjustments to the record offset and size + let wrapping = + std::mem::size_of::() as u64; + let size = data.len() as u64; + let offset = (res.record_offset + res.record_size) - size; + + // Add the entry + if file.write_from_mmap(res.record_offset, size).is_err() { + // We fall back on just writing the data normally + file.seek(io::SeekFrom::Start(offset as u64)).await; + file.write_all(&data).await?; + } + Ok(()) + }); + if let Err(err) = res { + tracing::trace!("fs::write err=EIO"); + reply.error(libc::EIO); + return; + } + } else { + tracing::trace!("fs::write err=EIO"); + reply.error(libc::EIO); + return; + } } reply.written(data.len() as u32); diff --git a/lib/compiler/Cargo.toml b/lib/compiler/Cargo.toml index 1adedf80f17..c38c4f09e83 100644 --- a/lib/compiler/Cargo.toml +++ b/lib/compiler/Cargo.toml @@ -34,7 +34,7 @@ enum-iterator = "0.7.0" bytes = "1.0" self_cell = "1.0" -rkyv = { version = "0.7.40", features = ["indexmap", "validation", "strict"] } +rkyv = { workspace = true } shared-buffer = { workspace = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/lib/journal/Cargo.toml b/lib/journal/Cargo.toml index 452454aef47..e6876efbb05 100644 --- a/lib/journal/Cargo.toml +++ b/lib/journal/Cargo.toml @@ -28,7 +28,7 @@ base64 = "0.21" bincode = { version = "1.3" } serde = { version = "1.0", default-features = false, features = ["derive"] } anyhow = "1.0" -rkyv = { version = "0.7.40", features = ["indexmap", "validation", "strict"] } +rkyv = { workspace = true } bytecheck = { version = "0.6.8" } lz4_flex = { version = "0.11" } num_enum = "0.5.7" diff --git a/lib/journal/src/concrete/arc.rs b/lib/journal/src/concrete/arc.rs index fa7a84146d8..1790777215a 100644 --- a/lib/journal/src/concrete/arc.rs +++ b/lib/journal/src/concrete/arc.rs @@ -3,7 +3,7 @@ use std::ops::Deref; use std::sync::Arc; impl ReadableJournal for Arc { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { self.deref().read() } @@ -13,13 +13,13 @@ impl ReadableJournal for Arc { } impl WritableJournal for Arc { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { self.deref().write(entry) } } impl ReadableJournal for Arc { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { self.deref().read() } @@ -29,7 +29,7 @@ impl ReadableJournal for Arc { } impl WritableJournal for Arc { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { self.deref().write(entry) } } diff --git a/lib/journal/src/concrete/boxed.rs b/lib/journal/src/concrete/boxed.rs index 75cb3de356a..9db8cdba87d 100644 --- a/lib/journal/src/concrete/boxed.rs +++ b/lib/journal/src/concrete/boxed.rs @@ -3,7 +3,7 @@ use std::ops::Deref; use super::*; impl ReadableJournal for Box { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { self.deref().read() } @@ -13,13 +13,13 @@ impl ReadableJournal for Box { } impl WritableJournal for Box { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { self.deref().write(entry) } } impl ReadableJournal for Box { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { self.deref().read() } @@ -29,7 +29,7 @@ impl ReadableJournal for Box { } impl WritableJournal for Box { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { self.deref().write(entry) } } diff --git a/lib/journal/src/concrete/buffered.rs b/lib/journal/src/concrete/buffered.rs index 67b16631120..12d34974f73 100644 --- a/lib/journal/src/concrete/buffered.rs +++ b/lib/journal/src/concrete/buffered.rs @@ -40,23 +40,29 @@ impl Default for BufferedJournal { } impl WritableJournal for BufferedJournalTx { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { let entry = entry.into_owned(); let state = self.state.lock().unwrap(); let estimate_size = entry.estimate_size(); state.records.lock().unwrap().push(entry); - Ok(estimate_size as u64) + Ok(LogWriteResult { + record_offset: state.offset as u64, + record_size: estimate_size as u64, + }) } } impl ReadableJournal for BufferedJournalRx { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { let mut state = self.state.lock().unwrap(); let ret = state.records.lock().unwrap().get(state.offset).cloned(); if ret.is_some() { state.offset += 1; } - Ok(ret) + Ok(ret.map(|r| LogReadResult { + record_offset: state.offset as u64, + record: r, + })) } fn as_restarted(&self) -> anyhow::Result> { @@ -69,13 +75,13 @@ impl ReadableJournal for BufferedJournalRx { } impl WritableJournal for BufferedJournal { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { self.tx.write(entry) } } impl ReadableJournal for BufferedJournal { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { self.rx.read() } diff --git a/lib/journal/src/concrete/compacting.rs b/lib/journal/src/concrete/compacting.rs index 8098c2ce2b5..a7bea8df3ac 100644 --- a/lib/journal/src/concrete/compacting.rs +++ b/lib/journal/src/concrete/compacting.rs @@ -252,9 +252,9 @@ impl CompactingJournalTx { // Read all the events and feed them into the filtered journal and then // strip off the filter so that its a normal journal again while let Some(entry) = replay_rx.read()? { - let amt = new_journal.write(entry)?; - if amt > 0 { - result.total_size += amt; + let res = new_journal.write(entry.into_inner())?; + if res.record_size > 0 { + result.total_size += res.record_size; result.total_events += 1; } } @@ -280,7 +280,7 @@ impl CompactingJournalTx { // extra events are added we strip off the filter again let replay_rx = state.inner_rx.as_restarted()?; while let Some(entry) = replay_rx.read()? { - new_journal.write(entry)?; + new_journal.write(entry.into_inner())?; } let new_journal = new_journal.into_inner(); @@ -301,7 +301,7 @@ impl CompactingJournalTx { } impl WritableJournal for CompactingJournalTx { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { let mut state = self.state.lock().unwrap(); let event_index = state.event_index; state.event_index += 1; @@ -517,7 +517,7 @@ impl CompactingJournal { } impl ReadableJournal for CompactingJournalRx { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { self.inner.read() } @@ -527,13 +527,13 @@ impl ReadableJournal for CompactingJournalRx { } impl WritableJournal for CompactingJournal { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { self.tx.write(entry) } } impl ReadableJournal for CompactingJournal { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { self.rx.read() } diff --git a/lib/journal/src/concrete/compacting_log_file.rs b/lib/journal/src/concrete/compacting_log_file.rs index 1face3b2ecd..4a21d35ee71 100644 --- a/lib/journal/src/concrete/compacting_log_file.rs +++ b/lib/journal/src/concrete/compacting_log_file.rs @@ -189,7 +189,7 @@ impl Drop for CompactingLogFileJournalTx { } impl ReadableJournal for CompactingLogFileJournalRx { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { self.inner.read() } @@ -199,14 +199,14 @@ impl ReadableJournal for CompactingLogFileJournalRx { } impl WritableJournal for CompactingLogFileJournalTx { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { - let amt = self.inner.write(entry)?; + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + let res = self.inner.write(entry)?; let triggered = { let mut state = self.state.lock().unwrap(); - if amt > 0 { + if res.record_size > 0 { state.cnt_records += 1; - state.cnt_size += amt; + state.cnt_size += res.record_size; } let mut triggered = false; @@ -235,12 +235,12 @@ impl WritableJournal for CompactingLogFileJournalTx { self.compact_now()?; } - Ok(amt) + Ok(res) } } impl ReadableJournal for CompactingLogFileJournal { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { self.rx.read() } @@ -250,7 +250,7 @@ impl ReadableJournal for CompactingLogFileJournal { } impl WritableJournal for CompactingLogFileJournal { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { self.tx.write(entry) } } diff --git a/lib/journal/src/concrete/counting.rs b/lib/journal/src/concrete/counting.rs index ddeda96a5a7..5a79807b3d8 100644 --- a/lib/journal/src/concrete/counting.rs +++ b/lib/journal/src/concrete/counting.rs @@ -23,7 +23,7 @@ impl CountingJournal { } impl ReadableJournal for CountingJournal { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { Ok(None) } @@ -33,11 +33,14 @@ impl ReadableJournal for CountingJournal { } impl WritableJournal for CountingJournal { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { let size = entry.estimate_size() as u64; self.n_cnt.fetch_add(1, Ordering::SeqCst); self.n_size.fetch_add(size, Ordering::SeqCst); - Ok(size) + Ok(LogWriteResult { + record_offset: 0, + record_size: size, + }) } } diff --git a/lib/journal/src/concrete/filter.rs b/lib/journal/src/concrete/filter.rs index 32a8ee3ce7c..f0cf670e8bc 100644 --- a/lib/journal/src/concrete/filter.rs +++ b/lib/journal/src/concrete/filter.rs @@ -207,11 +207,14 @@ impl FilteredJournal { } impl WritableJournal for FilteredJournalTx { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { let event_index = self.config.event_index.fetch_add(1, Ordering::SeqCst); if let Some(events) = self.config.filter_events.as_ref() { if !events.contains(&event_index) { - return Ok(0); + return Ok(LogWriteResult { + record_offset: 0, + record_size: 0, + }); } } @@ -223,19 +226,28 @@ impl WritableJournal for FilteredJournalTx { | JournalEntry::EpollCtlV1 { .. } | JournalEntry::TtySetV1 { .. } => { if self.config.filter_core { - return Ok(0); + return Ok(LogWriteResult { + record_offset: 0, + record_size: 0, + }); } entry } JournalEntry::SetThreadV1 { .. } | JournalEntry::CloseThreadV1 { .. } => { if self.config.filter_threads { - return Ok(0); + return Ok(LogWriteResult { + record_offset: 0, + record_size: 0, + }); } entry } JournalEntry::UpdateMemoryRegionV1 { .. } => { if self.config.filter_memory { - return Ok(0); + return Ok(LogWriteResult { + record_offset: 0, + record_size: 0, + }); } entry } @@ -254,10 +266,16 @@ impl WritableJournal for FilteredJournalTx { | JournalEntry::FileDescriptorSetTimesV1 { fd, .. } | JournalEntry::FileDescriptorSetSizeV1 { fd, .. } => { if self.config.filter_stdio && fd <= 2 { - return Ok(0); + return Ok(LogWriteResult { + record_offset: 0, + record_size: 0, + }); } if self.config.filter_fs { - return Ok(0); + return Ok(LogWriteResult { + record_offset: 0, + record_size: 0, + }); } entry } @@ -272,13 +290,19 @@ impl WritableJournal for FilteredJournalTx { | JournalEntry::CreatePipeV1 { .. } | JournalEntry::CreateEventV1 { .. } => { if self.config.filter_fs { - return Ok(0); + return Ok(LogWriteResult { + record_offset: 0, + record_size: 0, + }); } entry } JournalEntry::SnapshotV1 { .. } => { if self.config.filter_snapshots { - return Ok(0); + return Ok(LogWriteResult { + record_offset: 0, + record_size: 0, + }); } entry } @@ -309,7 +333,10 @@ impl WritableJournal for FilteredJournalTx { | JournalEntry::SocketSetOptTimeV1 { .. } | JournalEntry::SocketShutdownV1 { .. } => { if self.config.filter_net { - return Ok(0); + return Ok(LogWriteResult { + record_offset: 0, + record_size: 0, + }); } entry } @@ -319,7 +346,7 @@ impl WritableJournal for FilteredJournalTx { } impl ReadableJournal for FilteredJournalRx { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { self.inner.read() } @@ -331,13 +358,13 @@ impl ReadableJournal for FilteredJournalRx { } impl WritableJournal for FilteredJournal { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { self.tx.write(entry) } } impl ReadableJournal for FilteredJournal { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { self.rx.read() } diff --git a/lib/journal/src/concrete/log_file.rs b/lib/journal/src/concrete/log_file.rs index 767fb84db21..0430e49cb27 100644 --- a/lib/journal/src/concrete/log_file.rs +++ b/lib/journal/src/concrete/log_file.rs @@ -48,6 +48,12 @@ pub struct LogFileJournalRx { buffer: OwnedBuffer, } +impl LogFileJournalRx { + pub fn owned_buffer(&self) -> OwnedBuffer { + self.buffer.clone() + } +} + impl LogFileJournalTx { pub fn as_rx(&self) -> anyhow::Result { let state = self.state.lock().unwrap(); @@ -91,6 +97,10 @@ impl LogFileJournal { Self::from_file(file) } + pub fn owned_buffer(&self) -> OwnedBuffer { + self.rx.owned_buffer() + } + pub fn from_file(mut file: std::fs::File) -> anyhow::Result { // Move to the end of the file and write the // magic if one is needed @@ -123,7 +133,7 @@ impl LogFileJournal { } impl WritableJournal for LogFileJournalTx { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { tracing::debug!("journal event: {:?}", entry); let mut state = self.state.lock().unwrap(); @@ -150,17 +160,20 @@ impl WritableJournal for LogFileJournalTx { [a[0], a[1], b[0], b[1], b[2], b[3], b[4], b[5]] }; state.file.write_all(&header_bytes)?; - state.file.seek(SeekFrom::End(0))?; + state.file.seek(SeekFrom::Start(offset_end))?; // Now write the actual data and update the offsets - Ok(record_size) + Ok(LogWriteResult { + record_offset: offset_start, + record_size, + }) } } impl ReadableJournal for LogFileJournalRx { /// UNSAFE: This method uses unsafe operations to remove the need to zero /// the buffer before its read the log entries into it - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { let mut buffer_pos = self.buffer_pos.lock().unwrap(); // Get a memory reference to the data on the disk at @@ -214,7 +227,10 @@ impl ReadableJournal for LogFileJournalRx { }; let record = unsafe { record_type.deserialize_archive(entry)? }; - return Ok(Some(record)); + return Ok(Some(LogReadResult { + record_offset: *buffer_pos as u64, + record, + })); } } @@ -225,13 +241,13 @@ impl ReadableJournal for LogFileJournalRx { } impl WritableJournal for LogFileJournal { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { self.tx.write(entry) } } impl ReadableJournal for LogFileJournal { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { self.rx.read() } @@ -275,10 +291,10 @@ mod tests { // Read the events and validate let journal = LogFileJournal::new(file.path()).unwrap(); - let event1 = journal.read().unwrap(); - let event2 = journal.read().unwrap(); - let event3 = journal.read().unwrap(); - let event4 = journal.read().unwrap(); + let event1 = journal.read().unwrap().map(LogReadResult::into_inner); + let event2 = journal.read().unwrap().map(LogReadResult::into_inner); + let event3 = journal.read().unwrap().map(LogReadResult::into_inner); + let event4 = journal.read().unwrap().map(LogReadResult::into_inner); // Check the events assert_eq!(event1, Some(JournalEntry::CreatePipeV1 { fd1: 1, fd2: 2 })); @@ -306,7 +322,7 @@ mod tests { .unwrap(); // The event should not be visible yet unless we reload the log file - assert_eq!(journal.read().unwrap(), None); + assert_eq!(journal.read().unwrap().map(LogReadResult::into_inner), None); // Reload the load file let journal = LogFileJournal::new(file.path()).unwrap(); @@ -319,11 +335,11 @@ mod tests { }) .unwrap(); - let event1 = journal.read().unwrap(); - let event2 = journal.read().unwrap(); - let event3 = journal.read().unwrap(); - let event4 = journal.read().unwrap(); - let event5 = journal.read().unwrap(); + let event1 = journal.read().unwrap().map(LogReadResult::into_inner); + let event2 = journal.read().unwrap().map(LogReadResult::into_inner); + let event3 = journal.read().unwrap().map(LogReadResult::into_inner); + let event4 = journal.read().unwrap().map(LogReadResult::into_inner); + let event5 = journal.read().unwrap().map(LogReadResult::into_inner); assert_eq!(event1, Some(JournalEntry::CreatePipeV1 { fd1: 1, fd2: 2 })); assert_eq!( event2, @@ -350,12 +366,12 @@ mod tests { // Load it again let journal = LogFileJournal::new(file.path()).unwrap(); - let event1 = journal.read().unwrap(); - let event2 = journal.read().unwrap(); - let event3 = journal.read().unwrap(); - let event4 = journal.read().unwrap(); - let event5 = journal.read().unwrap(); - let event6 = journal.read().unwrap(); + let event1 = journal.read().unwrap().map(LogReadResult::into_inner); + let event2 = journal.read().unwrap().map(LogReadResult::into_inner); + let event3 = journal.read().unwrap().map(LogReadResult::into_inner); + let event4 = journal.read().unwrap().map(LogReadResult::into_inner); + let event5 = journal.read().unwrap().map(LogReadResult::into_inner); + let event6 = journal.read().unwrap().map(LogReadResult::into_inner); tracing::info!("event1 {:?}", event1); tracing::info!("event2 {:?}", event2); diff --git a/lib/journal/src/concrete/null.rs b/lib/journal/src/concrete/null.rs index 779e9d8ce28..61c822f6f56 100644 --- a/lib/journal/src/concrete/null.rs +++ b/lib/journal/src/concrete/null.rs @@ -9,7 +9,7 @@ pub struct NullJournal { } impl ReadableJournal for NullJournal { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { Ok(None) } @@ -19,11 +19,14 @@ impl ReadableJournal for NullJournal { } impl WritableJournal for NullJournal { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { if self.debug_print { tracing::debug!("journal event: {:?}", entry); } - Ok(entry.estimate_size() as u64) + Ok(LogWriteResult { + record_offset: 0, + record_size: entry.estimate_size() as u64, + }) } } diff --git a/lib/journal/src/concrete/pipe.rs b/lib/journal/src/concrete/pipe.rs index 1f9f7d15a31..8445b9d045b 100644 --- a/lib/journal/src/concrete/pipe.rs +++ b/lib/journal/src/concrete/pipe.rs @@ -14,12 +14,18 @@ pub struct PipeJournal { #[derive(Debug)] pub struct PipeJournalRx { - receiver: Arc>>>, + receiver: Arc>>>, +} + +#[derive(Debug)] +struct SenderState { + offset: u64, + sender: mpsc::Sender>, } #[derive(Debug)] pub struct PipeJournalTx { - sender: Arc>>>, + sender: Arc>, } impl PipeJournal { @@ -29,7 +35,10 @@ impl PipeJournal { let end1 = PipeJournal { tx: PipeJournalTx { - sender: Arc::new(Mutex::new(tx1)), + sender: Arc::new(Mutex::new(SenderState { + offset: 0, + sender: tx1, + })), }, rx: PipeJournalRx { receiver: Arc::new(Mutex::new(rx2)), @@ -38,7 +47,10 @@ impl PipeJournal { let end2 = PipeJournal { tx: PipeJournalTx { - sender: Arc::new(Mutex::new(tx2)), + sender: Arc::new(Mutex::new(SenderState { + offset: 0, + sender: tx2, + })), }, rx: PipeJournalRx { receiver: Arc::new(Mutex::new(rx1)), @@ -50,20 +62,30 @@ impl PipeJournal { } impl WritableJournal for PipeJournalTx { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { let entry = entry.into_owned(); - let entry_size = entry.estimate_size(); - - let sender = self.sender.lock().unwrap(); - sender.send(entry).map_err(|err| { - anyhow::format_err!("failed to send journal event through the pipe - {}", err) - })?; - Ok(entry_size as u64) + let entry_size = entry.estimate_size() as u64; + + let mut sender = self.sender.lock().unwrap(); + sender + .sender + .send(LogReadResult { + record_offset: sender.offset, + record: entry, + }) + .map_err(|err| { + anyhow::format_err!("failed to send journal event through the pipe - {}", err) + })?; + sender.offset += entry_size; + Ok(LogWriteResult { + record_offset: sender.offset, + record_size: entry_size, + }) } } impl ReadableJournal for PipeJournalRx { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { let rx = self.receiver.lock().unwrap(); match rx.try_recv() { Ok(e) => Ok(Some(e)), @@ -82,13 +104,13 @@ impl ReadableJournal for PipeJournalRx { } impl WritableJournal for PipeJournal { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { self.tx.write(entry) } } impl ReadableJournal for PipeJournal { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { self.rx.read() } diff --git a/lib/journal/src/concrete/printing.rs b/lib/journal/src/concrete/printing.rs index 0987c07ddd5..b69d25b5c05 100644 --- a/lib/journal/src/concrete/printing.rs +++ b/lib/journal/src/concrete/printing.rs @@ -29,7 +29,7 @@ impl PrintingJournal { } impl ReadableJournal for PrintingJournal { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { Ok(None) } @@ -39,14 +39,17 @@ impl ReadableJournal for PrintingJournal { } impl WritableJournal for PrintingJournal { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { match self.mode { JournalPrintingMode::Text => println!("{}", entry), JournalPrintingMode::Json => { println!("{}", serde_json::to_string_pretty(&entry)?) } } - Ok(entry.estimate_size() as u64) + Ok(LogWriteResult { + record_offset: 0, + record_size: entry.estimate_size() as u64, + }) } } diff --git a/lib/journal/src/concrete/recombined.rs b/lib/journal/src/concrete/recombined.rs index 05ae9e53cb9..6a97c9b44e1 100644 --- a/lib/journal/src/concrete/recombined.rs +++ b/lib/journal/src/concrete/recombined.rs @@ -12,13 +12,13 @@ impl RecombinedJournal { } impl WritableJournal for RecombinedJournal { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { self.tx.write(entry) } } impl ReadableJournal for RecombinedJournal { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { self.rx.read() } diff --git a/lib/journal/src/concrete/unsupported.rs b/lib/journal/src/concrete/unsupported.rs index a7e52a78f21..3ac2abd0b34 100644 --- a/lib/journal/src/concrete/unsupported.rs +++ b/lib/journal/src/concrete/unsupported.rs @@ -8,7 +8,7 @@ pub static UNSUPPORTED_JOURNAL: UnsupportedJournal = UnsupportedJournal {}; pub struct UnsupportedJournal {} impl ReadableJournal for UnsupportedJournal { - fn read(&self) -> anyhow::Result>> { + fn read(&self) -> anyhow::Result>> { Ok(None) } @@ -18,7 +18,7 @@ impl ReadableJournal for UnsupportedJournal { } impl WritableJournal for UnsupportedJournal { - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { tracing::debug!("journal event: {:?}", entry); Err(anyhow::format_err!("unsupported")) } diff --git a/lib/journal/src/lib.rs b/lib/journal/src/lib.rs index d92fa4cf8cf..b6889c20518 100644 --- a/lib/journal/src/lib.rs +++ b/lib/journal/src/lib.rs @@ -10,7 +10,16 @@ pub use snapshot::*; pub use util::*; use serde::{Deserialize, Serialize}; -use std::str::FromStr; +use std::{ops::Deref, str::FromStr}; + +/// The results of an operation to write a log entry to the log +#[derive(Debug)] +pub struct LogWriteResult { + // Start of the actual entry + pub record_offset: u64, + // End of the actual entry + pub record_size: u64, +} /// The snapshot capturer will take a series of objects that represents the state of /// a WASM process at a point in time and saves it so that it can be restored. @@ -19,7 +28,30 @@ use std::str::FromStr; pub trait WritableJournal { /// Takes in a stream of snapshot log entries and saves them so that they /// may be restored at a later moment - fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result; + fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result; +} + +/// The results of an operation to read a log entry from the log +#[derive(Debug)] +pub struct LogReadResult<'a> { + /// Offset into the journal where this entry exists + pub record_offset: u64, + /// Represents the journal entry + pub record: JournalEntry<'a>, +} + +impl<'a> LogReadResult<'a> { + pub fn into_inner(self) -> JournalEntry<'a> { + self.record + } +} + +impl<'a> Deref for LogReadResult<'a> { + type Target = JournalEntry<'a>; + + fn deref(&self) -> &Self::Target { + &self.record + } } /// The snapshot capturer will take a series of objects that represents the state of @@ -29,7 +61,7 @@ pub trait WritableJournal { pub trait ReadableJournal { /// Returns a stream of snapshot objects that the runtime will use /// to restore the state of a WASM process to a previous moment in time - fn read(&self) -> anyhow::Result>>; + fn read(&self) -> anyhow::Result>>; /// Resets the journal so that reads will start from the /// beginning again diff --git a/lib/journal/src/util.rs b/lib/journal/src/util.rs index 528c131fb2c..6f550a36622 100644 --- a/lib/journal/src/util.rs +++ b/lib/journal/src/util.rs @@ -5,7 +5,7 @@ pub fn copy_journal( to: &W, ) -> anyhow::Result<()> { while let Some(record) = from.read()? { - to.write(record)?; + to.write(record.into_inner())?; } Ok(()) } diff --git a/lib/types/Cargo.toml b/lib/types/Cargo.toml index 140c8c8b76b..2775e02e0cb 100644 --- a/lib/types/Cargo.toml +++ b/lib/types/Cargo.toml @@ -18,7 +18,7 @@ serde_bytes = { version = "0.11", optional = true } thiserror = "1.0" more-asserts = "0.2" indexmap = { version = "1.6" } -rkyv = { version = "0.7.40", features = ["indexmap", "validation", "strict"] } +rkyv = { workspace = true } enum-iterator = "0.7.0" target-lexicon = { version = "0.12.2", default-features = false } enumset.workspace = true diff --git a/lib/virtual-fs/src/lib.rs b/lib/virtual-fs/src/lib.rs index 591f086c44c..2f566c65af7 100644 --- a/lib/virtual-fs/src/lib.rs +++ b/lib/virtual-fs/src/lib.rs @@ -355,6 +355,12 @@ pub trait VirtualFile: None } + /// Writes to this file using an mmap offset and reference + /// (this method only works for mmap optimized file systems) + fn write_from_mmap(&mut self, _offset: u64, _len: u64) -> std::io::Result<()> { + Err(std::io::ErrorKind::Unsupported.into()) + } + /// This method will copy a file from a source to this destination where /// the default is to do a straight byte copy however file system implementors /// may optimize this to do a zero copy diff --git a/lib/virtual-fs/src/mem_fs/file.rs b/lib/virtual-fs/src/mem_fs/file.rs index 7ce38aac6b7..03d11d60fde 100644 --- a/lib/virtual-fs/src/mem_fs/file.rs +++ b/lib/virtual-fs/src/mem_fs/file.rs @@ -6,6 +6,8 @@ use futures::future::BoxFuture; use tokio::io::AsyncRead; use tokio::io::{AsyncSeek, AsyncWrite}; +use self::offloaded_file::OffloadWrite; + use super::*; use crate::limiter::TrackedVec; use crate::{CopyOnWriteFile, FsError, Result, VirtualFile}; @@ -420,6 +422,39 @@ impl VirtualFile for FileHandle { ))), } } + + fn write_from_mmap(&mut self, offset: u64, size: u64) -> std::io::Result<()> { + if !self.writable { + return Err(io::Error::new( + io::ErrorKind::PermissionDenied, + format!( + "the file (inode `{}) doesn't have the `write` permission", + self.inode + ), + )); + } + + let mut cursor = self.cursor; + { + let mut fs = self.filesystem.inner.write().map_err(|_| { + io::Error::new(io::ErrorKind::Other, "failed to acquire a write lock") + })?; + + let inode = fs.storage.get_mut(self.inode); + match inode { + Some(Node::OffloadedFile(node)) => { + node.file + .write(OffloadWrite::MmapOffset { offset, size }, &mut cursor)?; + node.metadata.len = node.file.len().try_into().unwrap(); + } + _ => { + return Err(io::ErrorKind::Unsupported.into()); + } + } + } + self.cursor = cursor; + Ok(()) + } } #[cfg(test)] @@ -832,7 +867,7 @@ impl AsyncWrite for FileHandle { bytes_written } Some(Node::OffloadedFile(node)) => { - let bytes_written = node.file.write(buf, &mut cursor)?; + let bytes_written = node.file.write(OffloadWrite::Buffer(buf), &mut cursor)?; node.metadata.len = node.file.len().try_into().unwrap(); bytes_written } @@ -916,7 +951,7 @@ impl AsyncWrite for FileHandle { .iter() .find(|b| !b.is_empty()) .map_or(&[][..], |b| &**b); - let bytes_written = node.file.write(buf, &mut cursor)?; + let bytes_written = node.file.write(OffloadWrite::Buffer(buf), &mut cursor)?; node.metadata.len = node.file.len(); Poll::Ready(Ok(bytes_written)) } diff --git a/lib/virtual-fs/src/mem_fs/file_opener.rs b/lib/virtual-fs/src/mem_fs/file_opener.rs index a45bd562749..6aa7f0b7321 100644 --- a/lib/virtual-fs/src/mem_fs/file_opener.rs +++ b/lib/virtual-fs/src/mem_fs/file_opener.rs @@ -502,7 +502,7 @@ impl crate::FileOpener for FileSystem { let inode_of_file = fs.storage.vacant_entry().key(); // We might be in optimized mode - let file = if let Some(offload) = fs.mmap_offload.clone() { + let file = if let Some(offload) = fs.backing_offload.clone() { let file = OffloadedFile::new(fs.limiter.clone(), offload); Node::OffloadedFile(OffloadedFileNode { inode: inode_of_file, diff --git a/lib/virtual-fs/src/mem_fs/filesystem.rs b/lib/virtual-fs/src/mem_fs/filesystem.rs index 4bbf26e07c0..419dfdf0092 100644 --- a/lib/virtual-fs/src/mem_fs/filesystem.rs +++ b/lib/virtual-fs/src/mem_fs/filesystem.rs @@ -1,9 +1,10 @@ //! This module contains the [`FileSystem`] type itself. +use self::offloaded_file::OffloadBackingStore; + use super::*; use crate::{DirEntry, FileSystem as _, FileType, FsError, Metadata, OpenOptions, ReadDir, Result}; use futures::future::BoxFuture; -use shared_buffer::OwnedBuffer; use slab::Slab; use std::collections::VecDeque; use std::convert::identity; @@ -37,9 +38,9 @@ impl FileSystem { /// data stored within the journals does not need to be copied /// into memory, for very large journals this would otherwise be /// a problem. - pub fn with_mmap_offload(self, buffer: OwnedBuffer) -> Result { + pub fn with_backing_offload(self, buffer: OffloadBackingStore) -> Result { let mut lock = self.inner.write().map_err(|_| FsError::Lock)?; - lock.mmap_offload.replace(buffer); + lock.backing_offload.replace(buffer); drop(lock); Ok(self) } @@ -634,7 +635,7 @@ impl fmt::Debug for FileSystem { /// indexed by their respective `Inode` in a slab. pub(super) struct FileSystemInner { pub(super) storage: Slab, - pub(super) mmap_offload: Option, + pub(super) backing_offload: Option, pub(super) limiter: Option, } @@ -1034,7 +1035,7 @@ impl Default for FileSystemInner { Self { storage: slab, - mmap_offload: None, + backing_offload: None, limiter: None, } } diff --git a/lib/virtual-fs/src/mem_fs/mod.rs b/lib/virtual-fs/src/mem_fs/mod.rs index 26d16e2b2c8..ab386f057c6 100644 --- a/lib/virtual-fs/src/mem_fs/mod.rs +++ b/lib/virtual-fs/src/mem_fs/mod.rs @@ -6,6 +6,7 @@ mod stdio; use file::{File, FileHandle, ReadOnlyFile}; pub use filesystem::FileSystem; +pub use offloaded_file::OffloadBackingStore; pub use stdio::{Stderr, Stdin, Stdout}; use crate::Metadata; diff --git a/lib/virtual-fs/src/mem_fs/offloaded_file.rs b/lib/virtual-fs/src/mem_fs/offloaded_file.rs index d4731589ff7..e5fb067a90f 100644 --- a/lib/virtual-fs/src/mem_fs/offloaded_file.rs +++ b/lib/virtual-fs/src/mem_fs/offloaded_file.rs @@ -1,6 +1,12 @@ use bytes::Bytes; use shared_buffer::OwnedBuffer; -use std::{cmp, io}; +use std::{ + cmp, + fs::File, + io, + ops::Range, + sync::{Arc, Mutex, MutexGuard}, +}; use crate::limiter::DynFsMemoryLimiter; @@ -32,17 +38,106 @@ impl FileExtent { } #[derive(Debug)] -pub struct OffloadedFile { +struct OffloadBackingStoreState { + mmap_file: Option, mmap_offload: OwnedBuffer, +} + +impl OffloadBackingStoreState { + fn get_slice(&mut self, range: Range) -> io::Result<&[u8]> { + let offset = range.start; + let size = match range.end { + u64::MAX => { + let len = self.mmap_offload.len() as u64; + if len < offset { + tracing::trace!("range out of bounds {} vs {}", len, offset); + return Err(io::ErrorKind::UnexpectedEof.into()); + } + len - offset + } + end => end - offset, + }; + + let end = offset + size; + if end > self.mmap_offload.len() as u64 { + let mmap_file = match self.mmap_file.as_ref() { + Some(a) => a, + None => { + tracing::trace!( + "mmap buffer out of bounds and no mmap file to reload {} vs {}", + end, + self.mmap_offload.len() + ); + return Err(io::ErrorKind::UnexpectedEof.into()); + } + }; + self.mmap_offload = OwnedBuffer::from_file(mmap_file) + .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?; + if end > self.mmap_offload.len() as u64 { + tracing::trace!( + "mmap buffer out of bounds {} vs {}", + end, + self.mmap_offload.len() + ); + return Err(io::ErrorKind::UnexpectedEof.into()); + } + } + Ok(&self.mmap_offload[offset as usize..end as usize]) + } +} + +#[derive(Debug, Clone)] +pub struct OffloadBackingStore { + state: Arc>, +} + +impl OffloadBackingStore { + pub fn new(mmap_offload: OwnedBuffer, mmap_file: Option) -> Self { + Self { + state: Arc::new(Mutex::new(OffloadBackingStoreState { + mmap_file, + mmap_offload, + })), + } + } + + pub fn from_file(filie: &File) -> Self { + let file = filie.try_clone().unwrap(); + let buffer = OwnedBuffer::from_file(&file).unwrap(); + Self::new(buffer, Some(file)) + } + + fn lock(&self) -> MutexGuard<'_, OffloadBackingStoreState> { + self.state.lock().unwrap() + } +} + +#[derive(Debug)] +pub struct OffloadedFile { + backing: OffloadBackingStore, #[allow(dead_code)] limiter: Option, extents: Vec, } +pub enum OffloadWrite<'a> { + MmapOffset { offset: u64, size: u64 }, + Buffer(&'a [u8]), +} + +impl<'a> OffloadWrite<'a> { + fn len(&self) -> usize { + match self { + OffloadWrite::MmapOffset { size, .. } => *size as usize, + OffloadWrite::Buffer(data) => data.len(), + } + } +} + impl OffloadedFile { - pub fn new(limiter: Option, buffer: OwnedBuffer) -> Self { + pub fn new(limiter: Option, backing: OffloadBackingStore) -> Self { Self { - mmap_offload: buffer, + backing, limiter, extents: Vec::new(), } @@ -94,9 +189,9 @@ impl OffloadedFile { offset: mmap_offset, size: extent_size, } => { + let mut backing = self.backing.lock(); let mmap_offset = mmap_offset + extent_offset; - let data = &self.mmap_offload.as_slice()[mmap_offset as usize..]; - let data = &data[..(*extent_size - extent_offset) as usize]; + let data = backing.get_slice(mmap_offset..(mmap_offset + *extent_size))?; let data_len = cmp::min(buf.len(), data.len()); buf[..data_len].copy_from_slice(&data[..data_len]); data_len @@ -124,7 +219,7 @@ impl OffloadedFile { Ok((*cursor - cursor_start) as usize) } - pub fn write(&mut self, data: &[u8], cursor: &mut u64) -> io::Result { + pub fn write(&mut self, data: OffloadWrite<'_>, cursor: &mut u64) -> io::Result { let mut extent_offset = *cursor; let mut data_len = data.len() as u64; @@ -191,25 +286,36 @@ impl OffloadedFile { self.extents.remove(index); } - // Finally we add the new extent - let data_start = data.as_ptr() as u64; - let data_end = data_start + data.len() as u64; - let mmap_start = self.mmap_offload.as_slice().as_ptr() as u64; - let mmap_end = mmap_start + self.mmap_offload.as_slice().len() as u64; - - // If the data is within the mmap buffer then we use a extent range - // to represent the data, otherwise we fall back on copying the data - let new_extent = if data_start >= mmap_start && data_end <= mmap_end { - FileExtent::MmapOffload { - offset: data_start - mmap_start, - size: data_end - data_start, + match data { + OffloadWrite::MmapOffset { offset, size } => { + self.extents + .insert(index, FileExtent::MmapOffload { offset, size }); } - } else { - FileExtent::InMemory { - data: data.to_vec().into(), + OffloadWrite::Buffer(data) => { + // Finally we add the new extent + let data_start = data.as_ptr() as u64; + let data_end = data_start + data.len() as u64; + let mut backing = self.backing.lock(); + let backing_data = backing.get_slice(0u64..u64::MAX)?; + + let mmap_start = backing_data.as_ptr() as u64; + let mmap_end = mmap_start + backing_data.len() as u64; + + // If the data is within the mmap buffer then we use a extent range + // to represent the data, otherwise we fall back on copying the data + let new_extent = if data_start >= mmap_start && data_end <= mmap_end { + FileExtent::MmapOffload { + offset: data_start - mmap_start, + size: data_end - data_start, + } + } else { + FileExtent::InMemory { + data: data.to_vec().into(), + } + }; + self.extents.insert(index, new_extent); } - }; - self.extents.insert(index, new_extent); + } // Update the cursor *cursor += data.len() as u64; @@ -258,11 +364,12 @@ mod tests { let buffer = OwnedBuffer::from_bytes(std::iter::repeat(12u8).take(100).collect::>()); let test_data2 = buffer.clone(); - let mut file = OffloadedFile::new(None, buffer); + let backing = OffloadBackingStore::new(buffer, None); + let mut file = OffloadedFile::new(None, backing); let mut cursor = 0u64; let test_data = std::iter::repeat(56u8).take(100).collect::>(); - file.write(&test_data, &mut cursor)?; + file.write(OffloadWrite::Buffer(&test_data), &mut cursor)?; assert_eq!(file.len(), 100); @@ -275,7 +382,7 @@ mod tests { ); cursor = 50; - file.write(&test_data2, &mut cursor)?; + file.write(OffloadWrite::Buffer(&test_data2), &mut cursor)?; assert_eq!(file.len(), 150); @@ -317,7 +424,7 @@ mod tests { let mut cursor = 10u64; let test_data = std::iter::repeat(74u8).take(10).collect::>(); - file.write(&test_data, &mut cursor)?; + file.write(OffloadWrite::Buffer(&test_data), &mut cursor)?; assert_eq!(file.len(), 33); diff --git a/lib/virtual-net/Cargo.toml b/lib/virtual-net/Cargo.toml index ed087b1ccdb..0438468bc4d 100644 --- a/lib/virtual-net/Cargo.toml +++ b/lib/virtual-net/Cargo.toml @@ -31,7 +31,7 @@ tokio-util = { version = "0.6", features = ["codec"], optional = true } hyper-tungstenite = { version = "0.11", optional = true } hyper = { version = "0.14", optional = true } tokio-tungstenite = { version = "0.20", optional = true } -rkyv = { version = "0.7.40", features = ["indexmap", "validation", "strict"], optional = true } +rkyv = { workspace = true, optional = true } bytecheck = { version = "0.6.8", optional = true } [dependencies.smoltcp] diff --git a/lib/wasix/Cargo.toml b/lib/wasix/Cargo.toml index 9d5fa1c7197..1eeba2e6abf 100644 --- a/lib/wasix/Cargo.toml +++ b/lib/wasix/Cargo.toml @@ -79,9 +79,9 @@ tower-http = { version = "0.4.0", features = [ ], optional = true } tower = { version = "0.4.13", features = ["make", "util"], optional = true } url = "2.3.1" -rkyv = { version = "0.7.40", features = ["indexmap", "validation", "strict"] } +rkyv = { workspace = true } bytecheck = "0.6.8" -shared-buffer = "0.1" +shared-buffer = { workspace = true } petgraph = "0.6.3" base64 = "0.21" lz4_flex = { version = "0.11" } diff --git a/lib/wasix/src/syscalls/journal.rs b/lib/wasix/src/syscalls/journal.rs index c904ed4c91e..5b85f68b99b 100644 --- a/lib/wasix/src/syscalls/journal.rs +++ b/lib/wasix/src/syscalls/journal.rs @@ -115,7 +115,7 @@ pub unsafe fn restore_snapshot( let mut staging_rewind = None; while let Some(next) = journal.read().map_err(anyhow_err_to_runtime_err)? { tracing::trace!("Restoring snapshot event - {next:?}"); - match next { + match next.into_inner() { crate::journal::JournalEntry::InitModuleV1 { wasm_hash } => { journal_module_hash.replace(wasm_hash); } From 8aa41693dcf3dee7487f4ffd38f6c4d4a8fb4489 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Wed, 24 Jan 2024 04:25:47 +1100 Subject: [PATCH 21/67] Significant speedup of the extent table loading --- Cargo.lock | 1 + lib/cli/src/commands/journal/mount/cmd.rs | 18 ++--- lib/cli/src/commands/journal/mount/fs.rs | 8 +-- lib/journal/Cargo.toml | 1 + lib/journal/src/concrete/log_file.rs | 16 ++++- lib/virtual-fs/src/mem_fs/offloaded_file.rs | 79 ++++++++++++++------- 6 files changed, 84 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f95054cc2f0..4381605bac2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6297,6 +6297,7 @@ dependencies = [ "thiserror", "tracing", "tracing-test", + "virtual-fs", "virtual-net 0.6.2", "wasmer", "wasmer-wasix-types", diff --git a/lib/cli/src/commands/journal/mount/cmd.rs b/lib/cli/src/commands/journal/mount/cmd.rs index 85da3b1289a..961099c4e4d 100644 --- a/lib/cli/src/commands/journal/mount/cmd.rs +++ b/lib/cli/src/commands/journal/mount/cmd.rs @@ -25,21 +25,21 @@ impl AsyncCliCommand for CmdJournalMount { impl CmdJournalMount { async fn run(self) -> Result<(), anyhow::Error> { + // First we unmount any existing file system on this path + std::process::Command::new("/bin/umount") + .arg(self.mount_path.to_string_lossy().as_ref()) + .stderr(Stdio::null()) + .stdout(Stdio::null()) + .spawn()? + .wait() + .ok(); + let fs = JournalFileSystemBuilder::new(&self.journal_path) .with_fd_seed(WasiFdSeed::default()) .with_progress_bar(true) .build()?; tokio::task::spawn_blocking(move || { - // First we unmount any existing file system on this path - std::process::Command::new("/bin/umount") - .arg(self.mount_path.to_string_lossy().as_ref()) - .stderr(Stdio::null()) - .stdout(Stdio::null()) - .spawn()? - .wait() - .ok(); - // Mounts the journal file system at a path fuse::mount(fs, &self.mount_path, &[])?; Ok(()) diff --git a/lib/cli/src/commands/journal/mount/fs.rs b/lib/cli/src/commands/journal/mount/fs.rs index 45892998b67..f100395106b 100644 --- a/lib/cli/src/commands/journal/mount/fs.rs +++ b/lib/cli/src/commands/journal/mount/fs.rs @@ -81,11 +81,11 @@ impl JournalFileSystemBuilder { // Opens the journal and copies all its contents into // and memory file system pub fn build(self) -> anyhow::Result { - let journal = LogFileJournal::new(&self.path)?; - let file = File::open(&self.path)?; - let mem_fs = mem_fs::FileSystem::default() - .with_backing_offload(OffloadBackingStore::from_file(&file))?; + let journal = LogFileJournal::from_file(file.try_clone()?)?; + let backing_store = journal.backing_store(); + + let mem_fs = mem_fs::FileSystem::default().with_backing_offload(backing_store)?; let state = MutexState { inner: Mutex::new(State { handle: tokio::runtime::Handle::current(), diff --git a/lib/journal/Cargo.toml b/lib/journal/Cargo.toml index e6876efbb05..4cc7c6b677b 100644 --- a/lib/journal/Cargo.toml +++ b/lib/journal/Cargo.toml @@ -17,6 +17,7 @@ log-file = [ "shared-buffer" ] wasmer = { default-features = false, path = "../api", version = "=4.2.5" } wasmer-wasix-types = { path = "../wasi-types", version = "0.18.0", features = [ "enable-serde" ] } virtual-net = { path = "../virtual-net", version = "0.6.2", default-features = false, features = ["rkyv"] } +virtual-fs = { path = "../virtual-fs" } shared-buffer = { workspace = true, optional = true } thiserror = "1" diff --git a/lib/journal/src/concrete/log_file.rs b/lib/journal/src/concrete/log_file.rs index 0430e49cb27..b352828c3d8 100644 --- a/lib/journal/src/concrete/log_file.rs +++ b/lib/journal/src/concrete/log_file.rs @@ -10,6 +10,7 @@ use std::{ path::Path, sync::{Arc, Mutex}, }; +use virtual_fs::mem_fs::OffloadBackingStore; use super::*; @@ -46,11 +47,16 @@ pub struct LogFileJournalRx { tx: LogFileJournalTx, buffer_pos: Mutex, buffer: OwnedBuffer, + store: OffloadBackingStore, } impl LogFileJournalRx { pub fn owned_buffer(&self) -> OwnedBuffer { - self.buffer.clone() + self.store.owned_buffer().clone() + } + + pub fn backing_store(&self) -> OffloadBackingStore { + self.store.clone() } } @@ -59,7 +65,8 @@ impl LogFileJournalTx { let state = self.state.lock().unwrap(); let file = state.file.try_clone()?; - let buffer = OwnedBuffer::from_file(&file)?; + let store = OffloadBackingStore::from_file(&file); + let buffer = store.owned_buffer(); // If the buffer exists we valid the magic number let mut buffer_pos = 0; @@ -83,6 +90,7 @@ impl LogFileJournalTx { tx: self.clone(), buffer_pos: Mutex::new(buffer_pos), buffer, + store, }) } } @@ -101,6 +109,10 @@ impl LogFileJournal { self.rx.owned_buffer() } + pub fn backing_store(&self) -> OffloadBackingStore { + self.rx.backing_store() + } + pub fn from_file(mut file: std::fs::File) -> anyhow::Result { // Move to the end of the file and write the // magic if one is needed diff --git a/lib/virtual-fs/src/mem_fs/offloaded_file.rs b/lib/virtual-fs/src/mem_fs/offloaded_file.rs index e5fb067a90f..dad3b34c595 100644 --- a/lib/virtual-fs/src/mem_fs/offloaded_file.rs +++ b/lib/virtual-fs/src/mem_fs/offloaded_file.rs @@ -101,12 +101,17 @@ impl OffloadBackingStore { } } - pub fn from_file(filie: &File) -> Self { - let file = filie.try_clone().unwrap(); + pub fn from_file(file: &File) -> Self { + let file = file.try_clone().unwrap(); let buffer = OwnedBuffer::from_file(&file).unwrap(); Self::new(buffer, Some(file)) } + pub fn owned_buffer(&self) -> OwnedBuffer { + let guard = self.state.lock().unwrap(); + guard.mmap_offload.clone() + } + fn lock(&self) -> MutexGuard<'_, OffloadBackingStoreState> { self.state.lock().unwrap() } @@ -118,6 +123,7 @@ pub struct OffloadedFile { #[allow(dead_code)] limiter: Option, extents: Vec, + size: u64, } pub enum OffloadWrite<'a> { @@ -140,6 +146,7 @@ impl OffloadedFile { backing, limiter, extents: Vec::new(), + size: 0, } } @@ -261,31 +268,52 @@ impl OffloadedFile { } } }; - split_extents(extent_offset); - split_extents(extent_offset + data_len); - - // Now we delete all the extents that exist between the - // range that we are about to insert - let mut index = 0usize; - while index < self.extents.len() { - let extent = &self.extents[index]; - if extent_offset >= extent.size() { - extent_offset -= extent.size(); - index += 1; - continue; - } else { - break; + + // If the extent is below the actual size of the file then we need to split it + let mut index = if extent_offset < self.size { + split_extents(extent_offset); + split_extents(extent_offset + data_len); + + // Now we delete all the extents that exist between the + // range that we are about to insert + let mut index = 0usize; + while index < self.extents.len() { + let extent = &self.extents[index]; + if extent_offset >= extent.size() { + extent_offset -= extent.size(); + index += 1; + continue; + } else { + break; + } } - } - while index < self.extents.len() { - let extent = &self.extents[index]; - if data_len < extent.size() { - break; + while index < self.extents.len() { + let extent = &self.extents[index]; + if data_len < extent.size() { + break; + } + data_len -= extent.size(); + self.extents.remove(index); } - data_len -= extent.size(); - self.extents.remove(index); + index + } else { + self.extents.len() + }; + + // If we have a gap that needs to be filled then do so + if extent_offset > self.size { + self.extents.insert( + index, + FileExtent::RepeatingBytes { + value: 0, + cnt: extent_offset - self.size, + }, + ); + self.size = extent_offset; + index += 1; } + // Insert the extent into the buffer match data { OffloadWrite::MmapOffset { offset, size } => { self.extents @@ -316,6 +344,7 @@ impl OffloadedFile { self.extents.insert(index, new_extent); } } + self.size = extent_offset + data_len; // Update the cursor *cursor += data.len() as u64; @@ -343,14 +372,16 @@ impl OffloadedFile { self.extents.pop(); } } + self.size = new_len; } pub fn len(&self) -> u64 { - self.extents.iter().map(FileExtent::size).sum() + self.size } pub fn truncate(&mut self) { self.extents.clear(); + self.size = 0; } } From ef2d7dca81c64be35c25e0b020014db46bfcac72 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Wed, 24 Jan 2024 09:25:54 +1100 Subject: [PATCH 22/67] Fixed a bug where file sizes could not be set --- lib/cli/src/commands/journal/mount/fs.rs | 411 ++++++++++++++++++++--- 1 file changed, 358 insertions(+), 53 deletions(-) diff --git a/lib/cli/src/commands/journal/mount/fs.rs b/lib/cli/src/commands/journal/mount/fs.rs index f100395106b..ddf605d11e9 100644 --- a/lib/cli/src/commands/journal/mount/fs.rs +++ b/lib/cli/src/commands/journal/mount/fs.rs @@ -12,15 +12,15 @@ use std::{ }; use fuse::{ - FileAttr, Filesystem, ReplyAttr, ReplyCreate, ReplyData, ReplyDirectory, ReplyEmpty, - ReplyEntry, ReplyOpen, ReplyWrite, Request, + FileAttr, Filesystem, ReplyAttr, ReplyBmap, ReplyCreate, ReplyData, ReplyDirectory, ReplyEmpty, + ReplyEntry, ReplyLock, ReplyOpen, ReplyStatfs, ReplyWrite, ReplyXattr, Request, }; use indicatif::{ProgressBar, ProgressStyle}; use shared_buffer::OwnedBuffer; use tokio::runtime::Handle; use virtual_fs::{ mem_fs::{self, OffloadBackingStore}, - AsyncReadExt, AsyncSeekExt, AsyncWriteExt, FileSystem, FsError, + AsyncReadExt, AsyncSeekExt, AsyncWriteExt, FileOpener, FileSystem, FsError, }; use wasmer_wasix::{ fs::WasiFdSeed, @@ -81,9 +81,9 @@ impl JournalFileSystemBuilder { // Opens the journal and copies all its contents into // and memory file system pub fn build(self) -> anyhow::Result { - let file = File::open(&self.path)?; - let journal = LogFileJournal::from_file(file.try_clone()?)?; + let journal = LogFileJournal::new(&self.path)?; let backing_store = journal.backing_store(); + let file_len = backing_store.owned_buffer().len(); let mem_fs = mem_fs::FileSystem::default().with_backing_offload(backing_store)?; let state = MutexState { @@ -98,9 +98,7 @@ impl JournalFileSystemBuilder { }; let progress = if self.progress_bar { - let file_len = file.metadata()?.len(); - - let mut pb = ProgressBar::new(file_len); + let mut pb = ProgressBar::new(file_len as u64); pb.set_style(ProgressStyle::with_template("{msg}\n{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})") .unwrap() .progress_chars("#>-")); @@ -138,7 +136,8 @@ pub fn copy_journal_with_progress( progress.set_position(record.record_offset); to.write(record.into_inner())?; } - progress.finish_with_message("Journal is Mounted"); + progress.finish_and_clear(); + println!("Journal is mounted"); Ok(()) } @@ -166,20 +165,20 @@ impl JournalFileSystem { Ok(path) } - fn attr<'a>(&self, name: Cow<'a, str>) -> Result { + fn attr<'a>(&self, path: Cow<'a, str>) -> Result { let mut state = self.state.inner.lock().unwrap(); - let res = state.mem_fs.metadata(&Path::new(name.as_ref())); + let res = state.mem_fs.metadata(&Path::new(path.as_ref())); match res { Ok(meta) => { // The ino is just the hash of the name let mut hasher = std::collections::hash_map::DefaultHasher::new(); - name.hash(&mut hasher); + path.hash(&mut hasher); let ino = hasher.finish(); state .inos .entry(ino) - .or_insert_with(|| name.into_owned().into()); + .or_insert_with(|| path.into_owned().into()); // Build a file attr and return it Ok(FileAttr { @@ -365,6 +364,12 @@ impl JournalFileSystem { } impl Filesystem for JournalFileSystem { + fn init(&mut self, _req: &Request) -> Result<(), libc::c_int> { + Ok(()) + } + + fn destroy(&mut self, _req: &Request) {} + fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { let path = match self.compute_path(parent, name) { Ok(a) => a, @@ -416,56 +421,166 @@ impl Filesystem for JournalFileSystem { _flags: Option, reply: ReplyAttr, ) { - let file = match fh { + let mut entries = Vec::new(); + + let attr = match fh { Some(fd) => { let fd = fd as u32; let mut state = self.state.inner.lock().unwrap(); - match state.lookup.get_mut(&fd) { + let file = match state.lookup.get_mut(&fd) { Some(f) => f.clone(), None => { tracing::trace!("fs::getattr noent (fd={fd})"); reply.error(libc::ENOENT); return; } - } + }; + + self.handle.block_on(async { + let mut file = file.lock().await; + + if let Some(size) = size { + entries.push(JournalEntry::FileDescriptorSetSizeV1 { + fd: fd as u32, + st_size: size, + }) + } + + FileAttr { + ino, + size: file.size(), + blocks: (1u64.max(file.size()) - 1 / 512) + 1, + atime: time::Timespec::new(file.last_accessed() as i64, 0), + mtime: time::Timespec::new(file.last_modified() as i64, 0), + ctime: time::Timespec::new(file.created_time() as i64, 0), + crtime: time::Timespec::new(file.created_time() as i64, 0), + kind: fuse::FileType::RegularFile, + perm: 0o644, + nlink: 1, + uid: 0, + gid: 0, + rdev: 0, + flags: 0, + } + }) } None => { - tracing::trace!("fs::getattr ino only is not supported"); - reply.error(libc::ENOSYS); - return; + let path = match self.reverse_ino(ino) { + Ok(a) => a, + Err(err) => { + tracing::trace!("fs::setattr reverse_ino({ino}) errno={err}"); + reply.error(err); + return; + } + }; + + let mut state = self.state.inner.lock().unwrap(); + let file = state + .mem_fs + .new_open_options() + .read(true) + .write(true) + .open(&Path::new(path.as_ref())); + let file = match file { + Ok(f) => f, + Err(err) => { + tracing::trace!("fs::setattr open_file({path}) err={err}"); + reply.error(libc::EIO); + return; + } + }; + drop(state); + + // Reserve a file descriptor + let fh = { + let mut state = self.state.inner.lock().unwrap(); + state.seed.next_val() + }; + + self.handle.block_on(async { + entries.push(JournalEntry::OpenFileDescriptorV1 { + fd: fh, + dirfd: VIRTUAL_ROOT_FD, + dirflags: 0, + path, + o_flags: wasi::Oflags::empty(), + fs_rights_base: wasi::Rights::all(), + fs_rights_inheriting: wasi::Rights::all(), + fs_flags: wasi::Fdflags::empty(), + }); + if let Some(size) = size { + entries.push(JournalEntry::FileDescriptorSetSizeV1 { + fd: fh as u32, + st_size: size, + }) + } + entries.push(JournalEntry::CloseFileDescriptorV1 { fd: fh }); + + FileAttr { + ino, + size: file.size(), + blocks: (1u64.max(file.size()) - 1 / 512) + 1, + atime: time::Timespec::new(file.last_accessed() as i64, 0), + mtime: time::Timespec::new(file.last_modified() as i64, 0), + ctime: time::Timespec::new(file.created_time() as i64, 0), + crtime: time::Timespec::new(file.created_time() as i64, 0), + kind: fuse::FileType::RegularFile, + perm: 0o644, + nlink: 1, + uid: 0, + gid: 0, + rdev: 0, + flags: 0, + } + }) } }; - // Read the data from the file and return it - let attr = self.handle.block_on(async { - let mut file = file.lock().await; - - if let Some(new_size) = size { - file.set_len(new_size); + for entry in entries.iter() { + if self.state.write(entry.clone()).is_err() { + tracing::trace!("fs::open err=EIO"); + reply.error(libc::EIO); + return; } - - FileAttr { - ino, - size: file.size(), - blocks: (1u64.max(file.size()) - 1 / 512) + 1, - atime: time::Timespec::new(file.last_accessed() as i64, 0), - mtime: time::Timespec::new(file.last_modified() as i64, 0), - ctime: time::Timespec::new(file.created_time() as i64, 0), - crtime: time::Timespec::new(file.created_time() as i64, 0), - kind: fuse::FileType::RegularFile, - perm: 0o644, - nlink: 1, - uid: 0, - gid: 0, - rdev: 0, - flags: 0, + } + for entry in entries.iter() { + if self.journal.write(entry.clone()).is_err() { + tracing::trace!("fs::open err=EIO"); + reply.error(libc::EIO); + return; } - }); + } // Return the data reply.attr(&time::Timespec::new(1, 0), &attr) } + fn setxattr( + &mut self, + _req: &Request, + _ino: u64, + _name: &OsStr, + _value: &[u8], + _flags: u32, + _position: u32, + reply: ReplyEmpty, + ) { + tracing::trace!("fs::setxattr err=ENOSYS"); + reply.error(libc::ENOSYS); + } + + fn getxattr( + &mut self, + _req: &Request, + _ino: u64, + _name: &OsStr, + _size: u32, + reply: ReplyXattr, + ) { + tracing::trace!("fs::getxattr size(0)"); + reply.size(0) + } + fn open(&mut self, _req: &Request, ino: u64, flags: u32, reply: ReplyOpen) { let path = match self.reverse_ino(ino) { Ok(a) => a, @@ -504,6 +619,7 @@ impl Filesystem for JournalFileSystem { return; } + tracing::trace!("fs::open opened fh={fh}"); reply.opened(fh as u64, flags); } @@ -542,6 +658,7 @@ impl Filesystem for JournalFileSystem { return; } + tracing::trace!("fs::release ok"); reply.ok(); } @@ -581,13 +698,13 @@ impl Filesystem for JournalFileSystem { fs_rights_inheriting: wasi::Rights::all(), fs_flags: wasi::Fdflags::empty(), }; - if self.state.write(entry.clone()).is_err() { - tracing::trace!("fs::create err=EIO"); + if let Err(err) = self.state.write(entry.clone()) { + tracing::trace!("fs::create (j1) err=EIO - {err}"); reply.error(libc::EIO); return; } - if self.journal.write(entry).is_err() { - tracing::trace!("fs::create err=EIO"); + if let Err(err) = self.journal.write(entry) { + tracing::trace!("fs::create (j2) err=EIO - {err}"); reply.error(libc::EIO); return; } @@ -696,8 +813,8 @@ impl Filesystem for JournalFileSystem { let res = match self.journal.write(entry) { Ok(res) => res, - Err(_) => { - tracing::trace!("fs::write err=EIO"); + Err(err) => { + tracing::trace!("fs::write err=EIO - {err}"); reply.error(libc::EIO); return; } @@ -821,7 +938,10 @@ impl Filesystem for JournalFileSystem { fn mkdir(&mut self, _req: &Request, parent: u64, name: &OsStr, _mode: u32, reply: ReplyEntry) { let path = match self.compute_path(parent, name) { Ok(a) => a, - Err(err) => return reply.error(err), + Err(err) => { + tracing::trace!("fs::mkdir compute_path err={err}"); + return reply.error(err); + } }; let entry = JournalEntry::CreateDirectoryV1 { @@ -832,15 +952,24 @@ impl Filesystem for JournalFileSystem { self.journal.write(entry); match self.attr(path) { - Ok(meta) => reply.entry(&time::Timespec::new(1, 0), &meta, 0), - Err(err) => reply.error(err), + Ok(meta) => { + tracing::trace!("fs::mkdir ok"); + reply.entry(&time::Timespec::new(1, 0), &meta, 0) + } + Err(err) => { + tracing::trace!("fs::mkdir attr err={err}"); + reply.error(err) + } } } fn rmdir(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { let path = match self.compute_path(parent, name) { Ok(a) => a, - Err(err) => return reply.error(err), + Err(err) => { + tracing::trace!("fs::rmdir err={err}"); + return reply.error(err); + } }; let entry = JournalEntry::RemoveDirectoryV1 { @@ -849,13 +978,17 @@ impl Filesystem for JournalFileSystem { }; self.state.write(entry.clone()); self.journal.write(entry); + tracing::trace!("fs::rmdir ok"); reply.ok(); } fn unlink(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEmpty) { let path = match self.compute_path(parent, name) { Ok(a) => a, - Err(err) => return reply.error(err), + Err(err) => { + tracing::trace!("fs::unlink err={err}"); + return reply.error(err); + } }; let entry = JournalEntry::UnlinkFileV1 { @@ -864,8 +997,180 @@ impl Filesystem for JournalFileSystem { }; self.state.write(entry.clone()); self.journal.write(entry); + tracing::trace!("fs::unlink ok"); reply.ok(); } + + fn forget(&mut self, _req: &Request, _ino: u64, _nlookup: u64) { + tracing::trace!("fs::forget ok"); + } + + fn readlink(&mut self, _req: &Request, _ino: u64, reply: ReplyData) { + tracing::trace!("fs::readlink err=ENOSYS"); + reply.error(libc::ENOSYS); + } + + fn mknod( + &mut self, + _req: &Request, + _parent: u64, + _name: &OsStr, + _mode: u32, + _rdev: u32, + reply: ReplyEntry, + ) { + tracing::trace!("fs::mknod err=ENOSYS"); + reply.error(libc::ENOSYS); + } + + fn symlink( + &mut self, + _req: &Request, + _parent: u64, + _name: &OsStr, + _link: &Path, + reply: ReplyEntry, + ) { + tracing::trace!("fs::symlink err=ENOSYS"); + reply.error(libc::ENOSYS); + } + + fn rename( + &mut self, + _req: &Request, + _parent: u64, + _name: &OsStr, + _newparent: u64, + _newname: &OsStr, + reply: ReplyEmpty, + ) { + tracing::trace!("fs::rename err=ENOSYS"); + reply.error(libc::ENOSYS); + } + + fn link( + &mut self, + _req: &Request, + _ino: u64, + _newparent: u64, + _newname: &OsStr, + reply: ReplyEntry, + ) { + tracing::trace!("fs::link err=ENOSYS"); + reply.error(libc::ENOSYS); + } + + fn fsync(&mut self, _req: &Request, _ino: u64, _fh: u64, _datasync: bool, reply: ReplyEmpty) { + tracing::trace!("fs::fsync err=ENOSYS"); + reply.error(libc::ENOSYS); + } + + fn opendir(&mut self, _req: &Request, _ino: u64, _flags: u32, reply: ReplyOpen) { + tracing::trace!("fs::opendir opened"); + reply.opened(0, 0); + } + + fn releasedir(&mut self, _req: &Request, _ino: u64, _fh: u64, _flags: u32, reply: ReplyEmpty) { + tracing::trace!("fs::releasedir ok"); + reply.ok(); + } + + fn fsyncdir( + &mut self, + _req: &Request, + _ino: u64, + _fh: u64, + _datasync: bool, + reply: ReplyEmpty, + ) { + tracing::trace!("fs::fsyncdir err=ENOSYS"); + reply.error(libc::ENOSYS); + } + + fn statfs(&mut self, _req: &Request, _ino: u64, reply: ReplyStatfs) { + tracing::trace!("fs::statfs ok"); + reply.statfs(0, 0, 0, 0, 0, 512, 255, 0); + } + + fn listxattr(&mut self, _req: &Request, _ino: u64, _size: u32, reply: ReplyXattr) { + tracing::trace!("fs::listxattr err=ENOSYS"); + reply.error(libc::ENOSYS); + } + + fn removexattr(&mut self, _req: &Request, _ino: u64, _name: &OsStr, reply: ReplyEmpty) { + tracing::trace!("fs::removexattr err=ENOSYS"); + reply.error(libc::ENOSYS); + } + + fn access(&mut self, _req: &Request, _ino: u64, _mask: u32, reply: ReplyEmpty) { + tracing::trace!("fs::access err=ENOSYS"); + reply.error(libc::ENOSYS); + } + + fn getlk( + &mut self, + _req: &Request, + _ino: u64, + _fh: u64, + _lock_owner: u64, + _start: u64, + _end: u64, + _typ: u32, + _pid: u32, + reply: ReplyLock, + ) { + tracing::trace!("fs::getlk err=ENOSYS"); + reply.error(libc::ENOSYS); + } + + fn setlk( + &mut self, + _req: &Request, + _ino: u64, + _fh: u64, + _lock_owner: u64, + _start: u64, + _end: u64, + _typ: u32, + _pid: u32, + _sleep: bool, + reply: ReplyEmpty, + ) { + tracing::trace!("fs::setlk err=ENOSYS"); + reply.error(libc::ENOSYS); + } + + fn bmap(&mut self, _req: &Request, _ino: u64, _blocksize: u32, _idx: u64, reply: ReplyBmap) { + tracing::trace!("fs::bmp err=ENOSYS"); + reply.error(libc::ENOSYS); + } + + #[cfg(target_os = "macos")] + fn setvolname(&mut self, _req: &Request, _name: &OsStr, reply: ReplyEmpty) { + tracing::trace!("fs::setvolname err=ENOSYS"); + reply.error(libc::ENOSYS); + } + + #[cfg(target_os = "macos")] + fn exchange( + &mut self, + _req: &Request, + _parent: u64, + _name: &OsStr, + _newparent: u64, + _newname: &OsStr, + _options: u64, + reply: ReplyEmpty, + ) { + tracing::trace!("fs::exchange err=ENOSYS"); + reply.error(libc::ENOSYS); + } + + #[cfg(target_os = "macos")] + fn getxtimes(&mut self, _req: &Request, _ino: u64, reply: ReplyXTimes) { + tracing::trace!("fs::getxtimes err=ENOSYS"); + reply.error(libc::ENOSYS); + } } fn file_type_to_kind(ft: virtual_fs::FileType) -> fuse::FileType { From 66374be8b2ee6288ce92433e0ff544c9434bc4e4 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Fri, 26 Jan 2024 09:31:22 +1100 Subject: [PATCH 23/67] Fixed the IO operations on file system --- lib/cli/src/commands/journal/mount/cmd.rs | 2 +- lib/cli/src/commands/journal/mount/fs.rs | 22 +++++++----- lib/journal/src/concrete/archived.rs | 6 ++++ lib/journal/src/concrete/buffered.rs | 9 +++-- lib/journal/src/concrete/compacting.rs | 4 +-- .../src/concrete/compacting_log_file.rs | 4 +-- lib/journal/src/concrete/counting.rs | 6 ++-- lib/journal/src/concrete/filter.rs | 36 +++++++++---------- lib/journal/src/concrete/log_file.rs | 8 +++-- lib/journal/src/concrete/null.rs | 4 +-- lib/journal/src/concrete/pipe.rs | 7 ++-- lib/journal/src/concrete/printing.rs | 4 +-- lib/journal/src/lib.rs | 14 ++++++-- lib/virtual-fs/src/mem_fs/offloaded_file.rs | 12 ++++--- 14 files changed, 83 insertions(+), 55 deletions(-) diff --git a/lib/cli/src/commands/journal/mount/cmd.rs b/lib/cli/src/commands/journal/mount/cmd.rs index 961099c4e4d..43d19d15c6d 100644 --- a/lib/cli/src/commands/journal/mount/cmd.rs +++ b/lib/cli/src/commands/journal/mount/cmd.rs @@ -36,7 +36,7 @@ impl CmdJournalMount { let fs = JournalFileSystemBuilder::new(&self.journal_path) .with_fd_seed(WasiFdSeed::default()) - .with_progress_bar(true) + .with_progress_bar(false) .build()?; tokio::task::spawn_blocking(move || { diff --git a/lib/cli/src/commands/journal/mount/fs.rs b/lib/cli/src/commands/journal/mount/fs.rs index ddf605d11e9..a701f3b830a 100644 --- a/lib/cli/src/commands/journal/mount/fs.rs +++ b/lib/cli/src/commands/journal/mount/fs.rs @@ -133,7 +133,7 @@ pub fn copy_journal_with_progress( mut progress: ProgressBar, ) -> anyhow::Result<()> { while let Some(record) = from.read()? { - progress.set_position(record.record_offset); + progress.set_position(record.record_end); to.write(record.into_inner())?; } progress.finish_and_clear(); @@ -208,10 +208,10 @@ impl WritableJournal for MutexState { fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { let mut state = self.inner.lock().unwrap(); let ret = LogWriteResult { - record_offset: state.fake_offset, - record_size: entry.estimate_size() as u64, + record_start: state.fake_offset, + record_end: state.fake_offset + entry.estimate_size() as u64, }; - state.fake_offset += ret.record_size; + state.fake_offset += ret.record_size(); match entry { JournalEntry::FileDescriptorWriteV1 { fd, @@ -832,14 +832,18 @@ impl Filesystem for JournalFileSystem { let mut file = file.lock().await; file.seek(io::SeekFrom::Start(offset as u64)).await; - // make sure adjustments to the record offset and size - let wrapping = - std::mem::size_of::() as u64; + // Unsafe!!! This assumes the structure does not change + // where the first bytes in the entry are an aligned + // array that corresponds to the data itself let size = data.len() as u64; - let offset = (res.record_offset + res.record_size) - size; + let mut mmap_offset = res.record_start; + let align = mmap_offset % 16; + if align != 0 { + mmap_offset += 16 - align; + } // Add the entry - if file.write_from_mmap(res.record_offset, size).is_err() { + if file.write_from_mmap(mmap_offset, size).is_err() { // We fall back on just writing the data normally file.seek(io::SeekFrom::Start(offset as u64)).await; file.write_all(&data).await?; diff --git a/lib/journal/src/concrete/archived.rs b/lib/journal/src/concrete/archived.rs index 9c5ba16a8d1..b7ddb28bfa8 100644 --- a/lib/journal/src/concrete/archived.rs +++ b/lib/journal/src/concrete/archived.rs @@ -969,11 +969,17 @@ pub struct JournalEntryFileDescriptorSeekV1 { pub offset: i64, } +/// WARNING!!!! Do not change this structure without updating +/// "/lib/cli/src/commands/journal/mount/fs.rs" +/// +/// The code over there assumes that the aligned vector is the +/// first item in the serialized entry #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] #[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntryFileDescriptorWriteV1<'a> { + /// DO NOT MOVE! pub data: AlignedCowVec<'a, u8>, pub offset: u64, pub fd: u32, diff --git a/lib/journal/src/concrete/buffered.rs b/lib/journal/src/concrete/buffered.rs index 12d34974f73..c5011c417c0 100644 --- a/lib/journal/src/concrete/buffered.rs +++ b/lib/journal/src/concrete/buffered.rs @@ -46,8 +46,8 @@ impl WritableJournal for BufferedJournalTx { let estimate_size = entry.estimate_size(); state.records.lock().unwrap().push(entry); Ok(LogWriteResult { - record_offset: state.offset as u64, - record_size: estimate_size as u64, + record_start: state.offset as u64, + record_end: state.offset as u64 + estimate_size as u64, }) } } @@ -56,11 +56,14 @@ impl ReadableJournal for BufferedJournalRx { fn read(&self) -> anyhow::Result>> { let mut state = self.state.lock().unwrap(); let ret = state.records.lock().unwrap().get(state.offset).cloned(); + + let record_start = state.offset as u64; if ret.is_some() { state.offset += 1; } Ok(ret.map(|r| LogReadResult { - record_offset: state.offset as u64, + record_start, + record_end: state.offset as u64, record: r, })) } diff --git a/lib/journal/src/concrete/compacting.rs b/lib/journal/src/concrete/compacting.rs index a7bea8df3ac..a7593c1c11c 100644 --- a/lib/journal/src/concrete/compacting.rs +++ b/lib/journal/src/concrete/compacting.rs @@ -253,8 +253,8 @@ impl CompactingJournalTx { // strip off the filter so that its a normal journal again while let Some(entry) = replay_rx.read()? { let res = new_journal.write(entry.into_inner())?; - if res.record_size > 0 { - result.total_size += res.record_size; + if res.record_size() > 0 { + result.total_size += res.record_size(); result.total_events += 1; } } diff --git a/lib/journal/src/concrete/compacting_log_file.rs b/lib/journal/src/concrete/compacting_log_file.rs index 4a21d35ee71..7bc33026ce2 100644 --- a/lib/journal/src/concrete/compacting_log_file.rs +++ b/lib/journal/src/concrete/compacting_log_file.rs @@ -204,9 +204,9 @@ impl WritableJournal for CompactingLogFileJournalTx { let triggered = { let mut state = self.state.lock().unwrap(); - if res.record_size > 0 { + if res.record_size() > 0 { state.cnt_records += 1; - state.cnt_size += res.record_size; + state.cnt_size += res.record_size(); } let mut triggered = false; diff --git a/lib/journal/src/concrete/counting.rs b/lib/journal/src/concrete/counting.rs index 5a79807b3d8..e7c32c038d1 100644 --- a/lib/journal/src/concrete/counting.rs +++ b/lib/journal/src/concrete/counting.rs @@ -35,11 +35,11 @@ impl ReadableJournal for CountingJournal { impl WritableJournal for CountingJournal { fn write<'a>(&'a self, entry: JournalEntry<'a>) -> anyhow::Result { let size = entry.estimate_size() as u64; - self.n_cnt.fetch_add(1, Ordering::SeqCst); + let offset = self.n_cnt.fetch_add(1, Ordering::SeqCst); self.n_size.fetch_add(size, Ordering::SeqCst); Ok(LogWriteResult { - record_offset: 0, - record_size: size, + record_start: offset as u64, + record_end: offset as u64 + size, }) } } diff --git a/lib/journal/src/concrete/filter.rs b/lib/journal/src/concrete/filter.rs index f0cf670e8bc..b89b2d9a6ba 100644 --- a/lib/journal/src/concrete/filter.rs +++ b/lib/journal/src/concrete/filter.rs @@ -212,8 +212,8 @@ impl WritableJournal for FilteredJournalTx { if let Some(events) = self.config.filter_events.as_ref() { if !events.contains(&event_index) { return Ok(LogWriteResult { - record_offset: 0, - record_size: 0, + record_start: 0, + record_end: 0, }); } } @@ -227,8 +227,8 @@ impl WritableJournal for FilteredJournalTx { | JournalEntry::TtySetV1 { .. } => { if self.config.filter_core { return Ok(LogWriteResult { - record_offset: 0, - record_size: 0, + record_start: 0, + record_end: 0, }); } entry @@ -236,8 +236,8 @@ impl WritableJournal for FilteredJournalTx { JournalEntry::SetThreadV1 { .. } | JournalEntry::CloseThreadV1 { .. } => { if self.config.filter_threads { return Ok(LogWriteResult { - record_offset: 0, - record_size: 0, + record_start: 0, + record_end: 0, }); } entry @@ -245,8 +245,8 @@ impl WritableJournal for FilteredJournalTx { JournalEntry::UpdateMemoryRegionV1 { .. } => { if self.config.filter_memory { return Ok(LogWriteResult { - record_offset: 0, - record_size: 0, + record_start: 0, + record_end: 0, }); } entry @@ -267,14 +267,14 @@ impl WritableJournal for FilteredJournalTx { | JournalEntry::FileDescriptorSetSizeV1 { fd, .. } => { if self.config.filter_stdio && fd <= 2 { return Ok(LogWriteResult { - record_offset: 0, - record_size: 0, + record_start: 0, + record_end: 0, }); } if self.config.filter_fs { return Ok(LogWriteResult { - record_offset: 0, - record_size: 0, + record_start: 0, + record_end: 0, }); } entry @@ -291,8 +291,8 @@ impl WritableJournal for FilteredJournalTx { | JournalEntry::CreateEventV1 { .. } => { if self.config.filter_fs { return Ok(LogWriteResult { - record_offset: 0, - record_size: 0, + record_start: 0, + record_end: 0, }); } entry @@ -300,8 +300,8 @@ impl WritableJournal for FilteredJournalTx { JournalEntry::SnapshotV1 { .. } => { if self.config.filter_snapshots { return Ok(LogWriteResult { - record_offset: 0, - record_size: 0, + record_start: 0, + record_end: 0, }); } entry @@ -334,8 +334,8 @@ impl WritableJournal for FilteredJournalTx { | JournalEntry::SocketShutdownV1 { .. } => { if self.config.filter_net { return Ok(LogWriteResult { - record_offset: 0, - record_size: 0, + record_start: 0, + record_end: 0, }); } entry diff --git a/lib/journal/src/concrete/log_file.rs b/lib/journal/src/concrete/log_file.rs index b352828c3d8..449b8ef3844 100644 --- a/lib/journal/src/concrete/log_file.rs +++ b/lib/journal/src/concrete/log_file.rs @@ -176,8 +176,8 @@ impl WritableJournal for LogFileJournalTx { // Now write the actual data and update the offsets Ok(LogWriteResult { - record_offset: offset_start, - record_size, + record_start: offset_start, + record_end: offset_end, }) } } @@ -220,6 +220,7 @@ impl ReadableJournal for LogFileJournalRx { *buffer_pos += 8; header }; + let record_start = *buffer_pos as u64; // Move the buffer position forward past the record let entry = &buffer_ptr[..(header.record_size as usize)]; @@ -240,7 +241,8 @@ impl ReadableJournal for LogFileJournalRx { let record = unsafe { record_type.deserialize_archive(entry)? }; return Ok(Some(LogReadResult { - record_offset: *buffer_pos as u64, + record_start, + record_end: *buffer_pos as u64, record, })); } diff --git a/lib/journal/src/concrete/null.rs b/lib/journal/src/concrete/null.rs index 61c822f6f56..f7b34e62abe 100644 --- a/lib/journal/src/concrete/null.rs +++ b/lib/journal/src/concrete/null.rs @@ -24,8 +24,8 @@ impl WritableJournal for NullJournal { tracing::debug!("journal event: {:?}", entry); } Ok(LogWriteResult { - record_offset: 0, - record_size: entry.estimate_size() as u64, + record_start: 0, + record_end: entry.estimate_size() as u64, }) } } diff --git a/lib/journal/src/concrete/pipe.rs b/lib/journal/src/concrete/pipe.rs index 8445b9d045b..94a8c851c9a 100644 --- a/lib/journal/src/concrete/pipe.rs +++ b/lib/journal/src/concrete/pipe.rs @@ -70,7 +70,8 @@ impl WritableJournal for PipeJournalTx { sender .sender .send(LogReadResult { - record_offset: sender.offset, + record_start: sender.offset, + record_end: sender.offset + entry_size, record: entry, }) .map_err(|err| { @@ -78,8 +79,8 @@ impl WritableJournal for PipeJournalTx { })?; sender.offset += entry_size; Ok(LogWriteResult { - record_offset: sender.offset, - record_size: entry_size, + record_start: sender.offset, + record_end: sender.offset + entry_size, }) } } diff --git a/lib/journal/src/concrete/printing.rs b/lib/journal/src/concrete/printing.rs index b69d25b5c05..168eab11264 100644 --- a/lib/journal/src/concrete/printing.rs +++ b/lib/journal/src/concrete/printing.rs @@ -47,8 +47,8 @@ impl WritableJournal for PrintingJournal { } } Ok(LogWriteResult { - record_offset: 0, - record_size: entry.estimate_size() as u64, + record_start: 0, + record_end: entry.estimate_size() as u64, }) } } diff --git a/lib/journal/src/lib.rs b/lib/journal/src/lib.rs index b6889c20518..c7e04c06ea3 100644 --- a/lib/journal/src/lib.rs +++ b/lib/journal/src/lib.rs @@ -16,9 +16,15 @@ use std::{ops::Deref, str::FromStr}; #[derive(Debug)] pub struct LogWriteResult { // Start of the actual entry - pub record_offset: u64, + pub record_start: u64, // End of the actual entry - pub record_size: u64, + pub record_end: u64, +} + +impl LogWriteResult { + pub fn record_size(&self) -> u64 { + self.record_end - self.record_start + } } /// The snapshot capturer will take a series of objects that represents the state of @@ -35,7 +41,9 @@ pub trait WritableJournal { #[derive(Debug)] pub struct LogReadResult<'a> { /// Offset into the journal where this entry exists - pub record_offset: u64, + pub record_start: u64, + /// Offset of the end of the entry + pub record_end: u64, /// Represents the journal entry pub record: JournalEntry<'a>, } diff --git a/lib/virtual-fs/src/mem_fs/offloaded_file.rs b/lib/virtual-fs/src/mem_fs/offloaded_file.rs index dad3b34c595..f95e482b767 100644 --- a/lib/virtual-fs/src/mem_fs/offloaded_file.rs +++ b/lib/virtual-fs/src/mem_fs/offloaded_file.rs @@ -75,9 +75,10 @@ impl OffloadBackingStoreState { .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?; if end > self.mmap_offload.len() as u64 { tracing::trace!( - "mmap buffer out of bounds {} vs {}", + "mmap buffer out of bounds {} vs {} for {:?}", end, - self.mmap_offload.len() + self.mmap_offload.len(), + range ); return Err(io::ErrorKind::UnexpectedEof.into()); } @@ -197,8 +198,11 @@ impl OffloadedFile { size: extent_size, } => { let mut backing = self.backing.lock(); - let mmap_offset = mmap_offset + extent_offset; - let data = backing.get_slice(mmap_offset..(mmap_offset + *extent_size))?; + let mmap_offset_plus_extent = mmap_offset + extent_offset; + let data = backing.get_slice( + mmap_offset_plus_extent + ..(mmap_offset_plus_extent + *extent_size - extent_offset), + )?; let data_len = cmp::min(buf.len(), data.len()); buf[..data_len].copy_from_slice(&data[..data_len]); data_len From 17be193746a27134912720759905fa2cf631e2b3 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 5 Feb 2024 22:05:03 +1100 Subject: [PATCH 24/67] Fixed the loading of mounted file systems with static web servers --- lib/cli/src/commands/journal/mount/fs.rs | 144 ++++++++++-------- lib/wasix/src/fs/mod.rs | 19 ++- .../syscalls/path_create_directory.rs | 26 +++- .../syscalls/path_remove_directory.rs | 20 ++- .../journal/effector/syscalls/path_rename.rs | 32 ++-- .../effector/syscalls/path_set_times.rs | 32 ++-- .../journal/effector/syscalls/path_unlink.rs | 26 +++- .../syscalls/wasi/path_create_directory.rs | 14 +- 8 files changed, 203 insertions(+), 110 deletions(-) diff --git a/lib/cli/src/commands/journal/mount/fs.rs b/lib/cli/src/commands/journal/mount/fs.rs index a701f3b830a..b4ed4920e10 100644 --- a/lib/cli/src/commands/journal/mount/fs.rs +++ b/lib/cli/src/commands/journal/mount/fs.rs @@ -474,6 +474,7 @@ impl Filesystem for JournalFileSystem { } }; + let fh; let mut state = self.state.inner.lock().unwrap(); let file = state .mem_fs @@ -481,76 +482,95 @@ impl Filesystem for JournalFileSystem { .read(true) .write(true) .open(&Path::new(path.as_ref())); - let file = match file { - Ok(f) => f, + match file { + Ok(file) => { + // Reserve a file descriptor and close the state + fh = state.seed.next_val(); + drop(state); + + entries.push(JournalEntry::OpenFileDescriptorV1 { + fd: fh, + dirfd: VIRTUAL_ROOT_FD, + dirflags: 0, + path, + o_flags: wasi::Oflags::empty(), + fs_rights_base: wasi::Rights::all(), + fs_rights_inheriting: wasi::Rights::all(), + fs_flags: wasi::Fdflags::empty(), + }); + if let Some(size) = size { + entries.push(JournalEntry::FileDescriptorSetSizeV1 { + fd: fh as u32, + st_size: size, + }) + } + entries.push(JournalEntry::CloseFileDescriptorV1 { fd: fh }); + + for entry in entries.iter() { + if self.state.write(entry.clone()).is_err() { + tracing::trace!("fs::open err=EIO"); + reply.error(libc::EIO); + return; + } + } + for entry in entries.iter() { + if self.journal.write(entry.clone()).is_err() { + tracing::trace!("fs::open err=EIO"); + reply.error(libc::EIO); + return; + } + } + FileAttr { + ino, + size: file.size(), + blocks: (1u64.max(file.size()) - 1 / 512) + 1, + atime: time::Timespec::new(file.last_accessed() as i64, 0), + mtime: time::Timespec::new(file.last_modified() as i64, 0), + ctime: time::Timespec::new(file.created_time() as i64, 0), + crtime: time::Timespec::new(file.created_time() as i64, 0), + kind: fuse::FileType::RegularFile, + perm: 0o644, + nlink: 1, + uid: 0, + gid: 0, + rdev: 0, + flags: 0, + } + } + Err(FsError::EntryNotFound) => { + // Maybe its a directory, in which case we are done + if let Ok(meta) = state.mem_fs.metadata(&Path::new(path.as_ref())) { + FileAttr { + ino, + size: meta.len, + blocks: (1u64.max(meta.len) - 1 / 512) + 1, + atime: time::Timespec::new(meta.accessed as i64, 0), + mtime: time::Timespec::new(meta.modified as i64, 0), + ctime: time::Timespec::new(meta.created as i64, 0), + crtime: time::Timespec::new(meta.created as i64, 0), + kind: file_type_to_kind(meta.ft), + perm: 0o644, + nlink: 1, + uid: 0, + gid: 0, + rdev: 0, + flags: 0, + } + } else { + tracing::trace!("fs::setattr open_file({path}) err=ENOENT"); + reply.error(libc::ENOENT); + return; + } + } Err(err) => { tracing::trace!("fs::setattr open_file({path}) err={err}"); reply.error(libc::EIO); return; } - }; - drop(state); - - // Reserve a file descriptor - let fh = { - let mut state = self.state.inner.lock().unwrap(); - state.seed.next_val() - }; - - self.handle.block_on(async { - entries.push(JournalEntry::OpenFileDescriptorV1 { - fd: fh, - dirfd: VIRTUAL_ROOT_FD, - dirflags: 0, - path, - o_flags: wasi::Oflags::empty(), - fs_rights_base: wasi::Rights::all(), - fs_rights_inheriting: wasi::Rights::all(), - fs_flags: wasi::Fdflags::empty(), - }); - if let Some(size) = size { - entries.push(JournalEntry::FileDescriptorSetSizeV1 { - fd: fh as u32, - st_size: size, - }) - } - entries.push(JournalEntry::CloseFileDescriptorV1 { fd: fh }); - - FileAttr { - ino, - size: file.size(), - blocks: (1u64.max(file.size()) - 1 / 512) + 1, - atime: time::Timespec::new(file.last_accessed() as i64, 0), - mtime: time::Timespec::new(file.last_modified() as i64, 0), - ctime: time::Timespec::new(file.created_time() as i64, 0), - crtime: time::Timespec::new(file.created_time() as i64, 0), - kind: fuse::FileType::RegularFile, - perm: 0o644, - nlink: 1, - uid: 0, - gid: 0, - rdev: 0, - flags: 0, - } - }) + } } }; - for entry in entries.iter() { - if self.state.write(entry.clone()).is_err() { - tracing::trace!("fs::open err=EIO"); - reply.error(libc::EIO); - return; - } - } - for entry in entries.iter() { - if self.journal.write(entry.clone()).is_err() { - tracing::trace!("fs::open err=EIO"); - reply.error(libc::EIO); - return; - } - } - // Return the data reply.attr(&time::Timespec::new(1, 0), &attr) } diff --git a/lib/wasix/src/fs/mod.rs b/lib/wasix/src/fs/mod.rs index b97b885eda7..fa367be15fb 100644 --- a/lib/wasix/src/fs/mod.rs +++ b/lib/wasix/src/fs/mod.rs @@ -1295,12 +1295,27 @@ impl WasiFs { } pub fn get_fd(&self, fd: WasiFd) -> Result { - self.fd_map + let ret = self + .fd_map .read() .unwrap() .get(&fd) .ok_or(Errno::Badf) - .map(|a| a.clone()) + .map(|a| a.clone()); + + if ret.is_err() && fd == VIRTUAL_ROOT_FD { + Ok(Fd { + rights: ALL_RIGHTS, + rights_inheriting: ALL_RIGHTS, + flags: Fdflags::empty(), + offset: Arc::new(AtomicU64::new(0)), + open_flags: 0, + inode: self.root_inode.clone(), + is_stdio: false, + }) + } else { + ret + } } pub fn get_fd_inode(&self, fd: WasiFd) -> Result { diff --git a/lib/wasix/src/journal/effector/syscalls/path_create_directory.rs b/lib/wasix/src/journal/effector/syscalls/path_create_directory.rs index 447c60393af..6400cf70d4d 100644 --- a/lib/wasix/src/journal/effector/syscalls/path_create_directory.rs +++ b/lib/wasix/src/journal/effector/syscalls/path_create_directory.rs @@ -1,3 +1,9 @@ +use std::path::Path; + +use virtual_fs::FileSystem; + +use crate::VIRTUAL_ROOT_FD; + use super::*; impl JournalEffector { @@ -20,14 +26,18 @@ impl JournalEffector { fd: Fd, path: &str, ) -> anyhow::Result<()> { - crate::syscalls::path_create_directory_internal(ctx, fd, path).map_err(|err| { - anyhow::format_err!( - "journal restore error: failed to create directory path (fd={}, path={}) - {}", - fd, - path, - err - ) - })?; + if fd == VIRTUAL_ROOT_FD { + ctx.data().state.fs.root_fs.create_dir(&Path::new(path))?; + } else { + crate::syscalls::path_create_directory_internal(ctx, fd, path).map_err(|err| { + anyhow::format_err!( + "journal restore error: failed to create directory path (fd={}, path={}) - {}", + fd, + path, + err + ) + })?; + } Ok(()) } } diff --git a/lib/wasix/src/journal/effector/syscalls/path_remove_directory.rs b/lib/wasix/src/journal/effector/syscalls/path_remove_directory.rs index b45ec07d1a4..e8c6b1d6d3f 100644 --- a/lib/wasix/src/journal/effector/syscalls/path_remove_directory.rs +++ b/lib/wasix/src/journal/effector/syscalls/path_remove_directory.rs @@ -1,3 +1,9 @@ +use std::path::Path; + +use virtual_fs::FileSystem; + +use crate::VIRTUAL_ROOT_FD; + use super::*; impl JournalEffector { @@ -20,11 +26,15 @@ impl JournalEffector { fd: Fd, path: &str, ) -> anyhow::Result<()> { - if let Err(err) = crate::syscalls::path_remove_directory_internal(ctx, fd, path) { - bail!( - "journal restore error: failed to remove directory - {}", - err - ); + if fd == VIRTUAL_ROOT_FD { + ctx.data().state.fs.root_fs.remove_dir(&Path::new(path))?; + } else { + if let Err(err) = crate::syscalls::path_remove_directory_internal(ctx, fd, path) { + bail!( + "journal restore error: failed to remove directory - {}", + err + ); + } } Ok(()) } diff --git a/lib/wasix/src/journal/effector/syscalls/path_rename.rs b/lib/wasix/src/journal/effector/syscalls/path_rename.rs index 41ca232f8b8..29434e19352 100644 --- a/lib/wasix/src/journal/effector/syscalls/path_rename.rs +++ b/lib/wasix/src/journal/effector/syscalls/path_rename.rs @@ -1,3 +1,5 @@ +use crate::{syscalls::__asyncify_light, VIRTUAL_ROOT_FD}; + use super::*; impl JournalEffector { @@ -26,16 +28,26 @@ impl JournalEffector { new_fd: Fd, new_path: &str, ) -> anyhow::Result<()> { - let ret = crate::syscalls::path_rename_internal(ctx, old_fd, old_path, new_fd, new_path)?; - if ret != Errno::Success { - bail!( - "journal restore error: failed to rename path (old_fd={}, old_path={}, new_fd={}, new_path={}) - {}", - old_fd, - old_path, - new_fd, - new_path, - ret - ); + if old_fd == VIRTUAL_ROOT_FD && new_fd == VIRTUAL_ROOT_FD { + let state = ctx.data().state.clone(); + let old_path = old_path.to_string(); + let new_path = new_path.to_string(); + __asyncify_light(ctx.data(), None, async move { + state.fs_rename(old_path, new_path).await + })??; + } else { + let ret = + crate::syscalls::path_rename_internal(ctx, old_fd, old_path, new_fd, new_path)?; + if ret != Errno::Success { + bail!( + "journal restore error: failed to rename path (old_fd={}, old_path={}, new_fd={}, new_path={}) - {}", + old_fd, + old_path, + new_fd, + new_path, + ret + ); + } } Ok(()) } diff --git a/lib/wasix/src/journal/effector/syscalls/path_set_times.rs b/lib/wasix/src/journal/effector/syscalls/path_set_times.rs index e40b3bae698..b22d643ee63 100644 --- a/lib/wasix/src/journal/effector/syscalls/path_set_times.rs +++ b/lib/wasix/src/journal/effector/syscalls/path_set_times.rs @@ -1,3 +1,5 @@ +use crate::VIRTUAL_ROOT_FD; + use super::*; impl JournalEffector { @@ -32,19 +34,23 @@ impl JournalEffector { st_mtim: Timestamp, fst_flags: Fstflags, ) -> anyhow::Result<()> { - crate::syscalls::path_filestat_set_times_internal(ctx, fd, flags, path, st_atim, st_mtim, fst_flags) - .map_err(|err| { - anyhow::format_err!( - "journal restore error: failed to set path times (fd={}, flags={}, path={}, st_atim={}, st_mtim={}, fst_flags={:?}) - {}", - fd, - flags, - path, - st_atim, - st_mtim, - fst_flags, - err - ) - })?; + if fd == VIRTUAL_ROOT_FD { + // we ignore this record as its not implemented yet + } else { + crate::syscalls::path_filestat_set_times_internal(ctx, fd, flags, path, st_atim, st_mtim, fst_flags) + .map_err(|err| { + anyhow::format_err!( + "journal restore error: failed to set path times (fd={}, flags={}, path={}, st_atim={}, st_mtim={}, fst_flags={:?}) - {}", + fd, + flags, + path, + st_atim, + st_mtim, + fst_flags, + err + ) + })?; + } Ok(()) } } diff --git a/lib/wasix/src/journal/effector/syscalls/path_unlink.rs b/lib/wasix/src/journal/effector/syscalls/path_unlink.rs index 9141a7fe8cf..4ca11f2297c 100644 --- a/lib/wasix/src/journal/effector/syscalls/path_unlink.rs +++ b/lib/wasix/src/journal/effector/syscalls/path_unlink.rs @@ -1,3 +1,9 @@ +use std::path::Path; + +use virtual_fs::FileSystem; + +use crate::VIRTUAL_ROOT_FD; + use super::*; impl JournalEffector { @@ -20,14 +26,18 @@ impl JournalEffector { fd: Fd, path: &str, ) -> anyhow::Result<()> { - let ret = crate::syscalls::path_unlink_file_internal(ctx, fd, path)?; - if ret != Errno::Success { - bail!( - "journal restore error: failed to remove file (fd={}, path={}) - {}", - fd, - path, - ret - ); + if fd == VIRTUAL_ROOT_FD { + ctx.data().state.fs.root_fs.remove_file(&Path::new(path))?; + } else { + let ret = crate::syscalls::path_unlink_file_internal(ctx, fd, path)?; + if ret != Errno::Success { + bail!( + "journal restore error: failed to remove file (fd={}, path={}) - {}", + fd, + path, + ret + ); + } } Ok(()) } diff --git a/lib/wasix/src/syscalls/wasi/path_create_directory.rs b/lib/wasix/src/syscalls/wasi/path_create_directory.rs index 8e514f43697..b7263ff21c5 100644 --- a/lib/wasix/src/syscalls/wasi/path_create_directory.rs +++ b/lib/wasix/src/syscalls/wasi/path_create_directory.rs @@ -60,10 +60,12 @@ pub(crate) fn path_create_directory_internal( { let guard = working_dir.inode.read(); if let Kind::Root { .. } = guard.deref() { + trace!("root has no rights to create a directories"); return Err(Errno::Access); } } if !working_dir.rights.contains(Rights::PATH_CREATE_DIRECTORY) { + trace!("working directory (fd={fd}) has no rights to create a directory"); return Err(Errno::Access); } @@ -78,6 +80,7 @@ pub(crate) fn path_create_directory_internal( }) .collect::, Errno>>()?; if path_vec.is_empty() { + trace!("path vector is inva;id (its empty)"); return Err(Errno::Inval); } @@ -118,6 +121,7 @@ pub(crate) fn path_create_directory_internal( &adjusted_path.to_string_lossy(), ) { if adjusted_path_stat.st_filetype != Filetype::Directory { + trace!("path is not a directory"); return Err(Errno::Notdir); } } else { @@ -145,8 +149,14 @@ pub(crate) fn path_create_directory_internal( cur_dir_inode = new_inode; } } - Kind::Root { .. } => return Err(Errno::Access), - _ => return Err(Errno::Notdir), + Kind::Root { .. } => { + trace!("the root node can no create a directory"); + return Err(Errno::Access); + } + _ => { + trace!("path is not a directory"); + return Err(Errno::Notdir); + } } } From 28b9519ee25fc400444929367e49531392101334 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 5 Feb 2024 23:40:29 +1100 Subject: [PATCH 25/67] Fixed a bug where journals that have incomplete write operations no longer crash the system --- lib/cli/src/commands/run/wasi.rs | 2 +- lib/journal/src/concrete/log_file.rs | 27 +++++++++++++++------------ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/cli/src/commands/run/wasi.rs b/lib/cli/src/commands/run/wasi.rs index d04c92e16ef..77c031c7376 100644 --- a/lib/cli/src/commands/run/wasi.rs +++ b/lib/cli/src/commands/run/wasi.rs @@ -156,7 +156,7 @@ pub struct Wasi { pub snapshot_on: Vec, /// Adds a periodic interval (measured in milli-seconds) that the runtime will automatically - /// takes snapshots of the running process and write them to the journal. When specifying + /// take snapshots of the running process and write them to the journal. When specifying /// this parameter it implies that `--snapshot-on interval` has also been specified. #[cfg(feature = "journal")] #[clap(long = "snapshot-period")] diff --git a/lib/journal/src/concrete/log_file.rs b/lib/journal/src/concrete/log_file.rs index 449b8ef3844..513a0bf9402 100644 --- a/lib/journal/src/concrete/log_file.rs +++ b/lib/journal/src/concrete/log_file.rs @@ -197,6 +197,8 @@ impl ReadableJournal for LogFileJournalRx { if buffer_ptr.len() < 8 { return Ok(None); } + + let record_type: JournalEntryRecordType; let header = { let b = buffer_ptr; @@ -216,6 +218,19 @@ impl ReadableJournal for LogFileJournalRx { record_type: u16::from_be_bytes([b[0], b[1]]), record_size: u64::from_be_bytes([0u8, 0u8, b[2], b[3], b[4], b[5], b[6], b[7]]), }; + + // Now we read the entry + record_type = match header.record_type.try_into() { + Ok(t) => t, + Err(_) => { + tracing::debug!( + "unknown journal entry type ({}) - the journal stops here", + header.record_type + ); + return Ok(None); + } + }; + buffer_ptr.advance(8); *buffer_pos += 8; header @@ -227,18 +242,6 @@ impl ReadableJournal for LogFileJournalRx { buffer_ptr.advance(header.record_size as usize); *buffer_pos += header.record_size as usize; - // Now we read the entry - let record_type: JournalEntryRecordType = match header.record_type.try_into() { - Ok(t) => t, - Err(_) => { - tracing::debug!( - "unknown journal entry type ({}) - skipping", - header.record_type - ); - continue; - } - }; - let record = unsafe { record_type.deserialize_archive(entry)? }; return Ok(Some(LogReadResult { record_start, From 00ac9fa17ae09e0e33aea32ff5d2d924c357e159 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 5 Feb 2024 23:54:48 +1100 Subject: [PATCH 26/67] Fixed an issue with the file system transversal on mounting file systems --- lib/wasix/src/fs/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/wasix/src/fs/mod.rs b/lib/wasix/src/fs/mod.rs index fa367be15fb..099e2f82563 100644 --- a/lib/wasix/src/fs/mod.rs +++ b/lib/wasix/src/fs/mod.rs @@ -1096,6 +1096,9 @@ impl WasiFs { if let Some(entry) = entries.get(component.as_ref()) { cur_inode = entry.clone(); + } else if let Some(root) = entries.get(&format!("/")) { + cur_inode = root.clone(); + continue 'symlink_resolution; } else { // Root is not capable of having something other then preopenned folders return Err(Errno::Notcapable); From b3dca80c094a36985b551b62c08e5a6252141217 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Tue, 6 Feb 2024 09:11:21 +1100 Subject: [PATCH 27/67] Fixed an issue where a terminating instance does not abort the connection attempt --- lib/wasix/src/runners/dproxy/factory.rs | 8 ++++-- .../runners/dproxy/hyper_proxy/connector.rs | 6 +++++ .../src/runners/dproxy/socket_manager.rs | 26 ++++++++++++++++++- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/lib/wasix/src/runners/dproxy/factory.rs b/lib/wasix/src/runners/dproxy/factory.rs index 042bdd25c3c..c7055684bc9 100644 --- a/lib/wasix/src/runners/dproxy/factory.rs +++ b/lib/wasix/src/runners/dproxy/factory.rs @@ -86,6 +86,7 @@ impl DProxyInstanceFactory { let this = self.clone(); let pkg = handler.config.pkg.clone(); let command_name = handler.command_name.clone(); + let connector_inner = connector.clone(); let runtime = Arc::new(runtime) as Arc; let mut runner = handler.config.inner.clone(); runtime @@ -99,8 +100,11 @@ impl DProxyInstanceFactory { } else { tracing::info!("Instance Exited: Nominal"); } - let mut state = this.state.lock().unwrap(); - state.instance.remove(&shard); + { + let mut state = this.state.lock().unwrap(); + state.instance.remove(&shard); + } + connector_inner.shutdown(); }))?; // Return an instance diff --git a/lib/wasix/src/runners/dproxy/hyper_proxy/connector.rs b/lib/wasix/src/runners/dproxy/hyper_proxy/connector.rs index 6d2943b6425..56adb560dd7 100644 --- a/lib/wasix/src/runners/dproxy/hyper_proxy/connector.rs +++ b/lib/wasix/src/runners/dproxy/hyper_proxy/connector.rs @@ -10,6 +10,12 @@ pub struct HyperProxyConnector { pub(super) socket_manager: Arc, } +impl HyperProxyConnector { + pub fn shutdown(&self) { + self.socket_manager.shutdown(); + } +} + impl Service for HyperProxyConnector { type Response = HyperProxyStream; type Error = BoxError; diff --git a/lib/wasix/src/runners/dproxy/socket_manager.rs b/lib/wasix/src/runners/dproxy/socket_manager.rs index 21aeb20e44b..1a0c3ffc81a 100644 --- a/lib/wasix/src/runners/dproxy/socket_manager.rs +++ b/lib/wasix/src/runners/dproxy/socket_manager.rs @@ -10,6 +10,7 @@ use std::{ }; use derivative::Derivative; +use tokio::sync::broadcast; use virtual_net::{tcp_pair::TcpSocketHalf, LoopbackNetworking}; pub type PollListeningFn = @@ -24,6 +25,8 @@ pub struct SocketManager { proxy_connect_init_timeout: Duration, proxy_connect_nominal_timeout: Duration, is_running: AtomicBool, + is_terminated: AtomicBool, + terminate_all: broadcast::Sender<()>, } impl SocketManager { @@ -39,17 +42,38 @@ impl SocketManager { proxy_connect_init_timeout, proxy_connect_nominal_timeout, is_running: AtomicBool::new(false), + is_terminated: AtomicBool::new(false), + terminate_all: broadcast::channel(1).0, } } + pub fn shutdown(&self) { + self.is_terminated.store(true, Ordering::SeqCst); + self.terminate_all.send(()).ok(); + } + pub async fn acquire_http_socket(&self) -> anyhow::Result { + let mut rx_terminate = self.terminate_all.subscribe(); + + if self.is_terminated.load(Ordering::SeqCst) { + return Err(anyhow::anyhow!( + "failed to open HTTP socket as the instance has terminated" + )); + } let connect_timeout = if self.is_running.load(Ordering::SeqCst) == true { self.proxy_connect_nominal_timeout } else { self.proxy_connect_init_timeout }; - let ret = tokio::time::timeout(connect_timeout, self.open_proxy_http_socket()).await??; + let ret = tokio::select! { + socket = tokio::time::timeout(connect_timeout, self.open_proxy_http_socket()) => socket??, + _ = rx_terminate.recv() => { + return Err(anyhow::anyhow!( + "failed to open HTTP socket as the instance has terminated" + )); + } + }; self.is_running.store(true, Ordering::Relaxed); Ok(ret) } From 45d98097b42bf4a7256858e8dffe0fb99f722179 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Tue, 6 Feb 2024 21:16:43 +1100 Subject: [PATCH 28/67] DProxy now correctly snapshots threads to the journal --- lib/wasix/src/os/task/process.rs | 36 ++++++++---- lib/wasix/src/os/task/thread.rs | 16 +++++- lib/wasix/src/runtime/task_manager/mod.rs | 2 +- lib/wasix/src/syscalls/journal.rs | 54 ++++++++++++++---- lib/wasix/src/syscalls/mod.rs | 58 ++++++++++++++++++-- lib/wasix/src/syscalls/wasi/poll_oneoff.rs | 1 - lib/wasix/src/syscalls/wasix/epoll_wait.rs | 1 - lib/wasix/src/syscalls/wasix/futex_wait.rs | 3 +- lib/wasix/src/syscalls/wasix/proc_exec.rs | 18 +++--- lib/wasix/src/syscalls/wasix/proc_join.rs | 52 ++++++++---------- lib/wasix/src/syscalls/wasix/thread_join.rs | 23 ++++---- lib/wasix/src/syscalls/wasix/thread_sleep.rs | 7 +-- 12 files changed, 181 insertions(+), 90 deletions(-) diff --git a/lib/wasix/src/os/task/process.rs b/lib/wasix/src/os/task/process.rs index d6cb91ec181..6e627022346 100644 --- a/lib/wasix/src/os/task/process.rs +++ b/lib/wasix/src/os/task/process.rs @@ -11,6 +11,7 @@ use std::{ atomic::{AtomicU32, Ordering}, Arc, Condvar, Mutex, MutexGuard, RwLock, Weak, }, + task::Waker, time::Duration, }; use tracing::trace; @@ -91,7 +92,7 @@ pub struct WasiProcess { /// List of all the children spawned from this thread pub(crate) parent: Option>>, /// The inner protected region of the process with a conditional - /// variable that is used for coordination such as checksums. + /// variable that is used for coordination such as snapshots. pub(crate) inner: LockableWasiProcessInner, /// Reference back to the compute engine // TODO: remove this reference, access should happen via separate state instead @@ -133,6 +134,8 @@ pub struct WasiProcessInner { /// Represents a checkpoint which blocks all the threads /// and then executes some maintenance action pub checkpoint: WasiProcessCheckpoint, + /// Any wakers waiting on this process (for example for a checkpoint) + pub wakers: Vec, } pub enum MaybeCheckpointResult<'a> { @@ -151,8 +154,12 @@ impl WasiProcessInner { ) -> WasiResult> { // Set the checkpoint flag and then enter the normal processing loop { - let mut inner = inner.0.lock().unwrap(); - inner.checkpoint = for_what; + let mut guard = inner.0.lock().unwrap(); + guard.checkpoint = for_what; + for waker in guard.wakers.drain(..) { + waker.wake(); + } + inner.1.notify_all(); } Self::maybe_checkpoint::(inner, ctx) @@ -161,10 +168,10 @@ impl WasiProcessInner { /// If a checkpoint has been started this will block the current process /// until the checkpoint operation has completed #[cfg(feature = "journal")] - pub fn maybe_checkpoint( + pub fn maybe_checkpoint<'a, M: wasmer_types::MemorySize>( inner: LockableWasiProcessInner, - ctx: FunctionEnvMut<'_, WasiEnv>, - ) -> WasiResult> { + ctx: FunctionEnvMut<'a, WasiEnv>, + ) -> WasiResult> { // Enter the lock which will determine if we are in a checkpoint or not use bytes::Bytes; @@ -215,10 +222,13 @@ impl WasiProcessInner { // to freeze then we have to execute the checksum operation) loop { if let WasiProcessCheckpoint::Snapshot { trigger } = guard.checkpoint { - ctx.data().thread.set_check_pointing(true); + ctx.data().thread.set_checkpointing(true); // Now if we are the last thread we also write the memory - let is_last_thread = guard.threads.values().all(WasiThread::is_check_pointing); + let is_last_thread = guard + .threads + .values() + .all(|t| t.is_check_pointing() || t.is_deep_sleeping()); if is_last_thread { if let Err(err) = JournalEffector::save_memory_and_snapshot(&mut ctx, &mut guard, trigger) @@ -228,9 +238,12 @@ impl WasiProcessInner { } // Clear the checkpointing flag and notify everyone to wake up - ctx.data().thread.set_check_pointing(false); - guard.checkpoint = WasiProcessCheckpoint::Execute; + ctx.data().thread.set_checkpointing(false); trace!("checkpoint complete"); + guard.checkpoint = WasiProcessCheckpoint::Execute; + for waker in guard.wakers.drain(..) { + waker.wake(); + } inner.1.notify_all(); } else { guard = inner.1.wait(guard).unwrap(); @@ -238,7 +251,7 @@ impl WasiProcessInner { continue; } - ctx.data().thread.set_check_pointing(false); + ctx.data().thread.set_checkpointing(false); trace!("checkpoint finished"); // Rewind the stack and carry on @@ -295,6 +308,7 @@ impl WasiProcess { signal_intervals: Default::default(), children: Default::default(), checkpoint: WasiProcessCheckpoint::Execute, + wakers: Default::default(), }), Condvar::new(), )), diff --git a/lib/wasix/src/os/task/thread.rs b/lib/wasix/src/os/task/thread.rs index ffcc091ec21..e6de82e89b3 100644 --- a/lib/wasix/src/os/task/thread.rs +++ b/lib/wasix/src/os/task/thread.rs @@ -131,10 +131,22 @@ impl WasiThread { } } + /// Sets a flag that tells others if this thread is currently + /// deep sleeping + pub(crate) fn set_deep_sleeping(&self, val: bool) { + self.state.deep_sleeping.store(val, Ordering::SeqCst); + } + + /// Reads a flag that determines if this thread is currently + /// deep sleeping + pub(crate) fn is_deep_sleeping(&self) -> bool { + self.state.deep_sleeping.load(Ordering::SeqCst) + } + /// Sets a flag that tells others that this thread is currently /// check pointing itself #[cfg(feature = "journal")] - pub(crate) fn set_check_pointing(&self, val: bool) { + pub(crate) fn set_checkpointing(&self, val: bool) { self.state.check_pointing.store(val, Ordering::SeqCst); } @@ -217,6 +229,7 @@ struct WasiThreadState { status: Arc, #[cfg(feature = "journal")] check_pointing: AtomicBool, + deep_sleeping: AtomicBool, // Registers the task termination with the ControlPlane on drop. // Never accessed, since it's a drop guard. @@ -244,6 +257,7 @@ impl WasiThread { stack: Mutex::new(ThreadStack::default()), #[cfg(feature = "journal")] check_pointing: AtomicBool::new(false), + deep_sleeping: AtomicBool::new(false), _task_count_guard: guard, }), layout, diff --git a/lib/wasix/src/runtime/task_manager/mod.rs b/lib/wasix/src/runtime/task_manager/mod.rs index a0de8d44c49..2f0f9ac7000 100644 --- a/lib/wasix/src/runtime/task_manager/mod.rs +++ b/lib/wasix/src/runtime/task_manager/mod.rs @@ -390,7 +390,7 @@ impl dyn VirtualTaskManager { } }; - tracing::trace!("deep sleep woken - {:?}", res); + tracing::trace!("deep sleep woken - res.len={}", res.len()); Ok(res) }) })), diff --git a/lib/wasix/src/syscalls/journal.rs b/lib/wasix/src/syscalls/journal.rs index 5b85f68b99b..8213e3cb1e9 100644 --- a/lib/wasix/src/syscalls/journal.rs +++ b/lib/wasix/src/syscalls/journal.rs @@ -2,18 +2,18 @@ use super::*; #[allow(clippy::extra_unused_type_parameters)] #[cfg(not(feature = "journal"))] -pub fn maybe_snapshot_once( - ctx: FunctionEnvMut<'_, WasiEnv>, +pub fn maybe_snapshot_once<'a, M: MemorySize>( + ctx: FunctionEnvMut<'a, WasiEnv>, _trigger: crate::journal::SnapshotTrigger, -) -> WasiResult> { +) -> WasiResult> { Ok(Ok(ctx)) } #[cfg(feature = "journal")] -pub fn maybe_snapshot_once( - mut ctx: FunctionEnvMut<'_, WasiEnv>, +pub fn maybe_snapshot_once<'a, M: MemorySize>( + mut ctx: FunctionEnvMut<'a, WasiEnv>, trigger: crate::journal::SnapshotTrigger, -) -> WasiResult> { +) -> WasiResult> { use crate::os::task::process::{WasiProcessCheckpoint, WasiProcessInner}; if unsafe { handle_rewind_ext_with_default::(&mut ctx, HandleRewindType::ResultLess) } @@ -45,16 +45,16 @@ pub fn maybe_snapshot_once( #[allow(clippy::extra_unused_type_parameters)] #[cfg(not(feature = "journal"))] -pub fn maybe_snapshot( - ctx: FunctionEnvMut<'_, WasiEnv>, -) -> WasiResult> { +pub fn maybe_snapshot<'a, M: MemorySize>( + ctx: FunctionEnvMut<'a, WasiEnv>, +) -> WasiResult> { Ok(Ok(ctx)) } #[cfg(feature = "journal")] -pub fn maybe_snapshot( - mut ctx: FunctionEnvMut<'_, WasiEnv>, -) -> WasiResult> { +pub fn maybe_snapshot<'a, M: MemorySize>( + mut ctx: FunctionEnvMut<'a, WasiEnv>, +) -> WasiResult> { use crate::os::task::process::{WasiProcessCheckpoint, WasiProcessInner}; if !ctx.data().enable_journal { @@ -72,6 +72,36 @@ pub fn maybe_snapshot( Ok(Ok(ctx)) } +#[cfg(not(feature = "journal"))] +pub fn wait_for_snapshot(_env: &WasiEnv) -> Pin>> { + Box::pin(std::future::pending()) +} + +#[cfg(feature = "journal")] +pub fn wait_for_snapshot(env: &WasiEnv) -> Pin>> { + use crate::os::task::process::{LockableWasiProcessInner, WasiProcessCheckpoint}; + + struct Poller { + inner: LockableWasiProcessInner, + } + impl Future for Poller { + type Output = (); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut guard = self.inner.0.lock().unwrap(); + if !matches!(guard.checkpoint, WasiProcessCheckpoint::Execute) { + return Poll::Ready(()); + } + if !guard.wakers.iter().any(|w| w.will_wake(cx.waker())) { + guard.wakers.push(cx.waker().clone()); + } + Poll::Pending + } + } + Box::pin(Poller { + inner: env.process.inner.clone(), + }) +} + /// Safety: This function manipulates the memory of the process and thus must /// be executed by the WASM process thread itself. /// diff --git a/lib/wasix/src/syscalls/mod.rs b/lib/wasix/src/syscalls/mod.rs index 94ce49ee40e..4d1d916036c 100644 --- a/lib/wasix/src/syscalls/mod.rs +++ b/lib/wasix/src/syscalls/mod.rs @@ -412,13 +412,18 @@ pub enum AsyncifyAction<'a, R> { /// pub(crate) fn __asyncify_with_deep_sleep( mut ctx: FunctionEnvMut<'_, WasiEnv>, - deep_sleep_time: Duration, work: Fut, ) -> Result, WasiError> where T: serde::Serialize + serde::de::DeserializeOwned, Fut: Future + Send + Sync + 'static, { + // Determine the deep sleep time + let deep_sleep_time = match ctx.data().enable_journal { + true => Duration::from_micros(100), + false => Duration::from_millis(50), + }; + // Determine if we should process signals or now let process_signals = ctx .data() @@ -440,6 +445,7 @@ where } else { None }; + let deep_sleep_wait = async { if let Some(tasks) = tasks_for_deep_sleep { tasks.sleep_now(deep_sleep_time).await @@ -462,9 +468,16 @@ where _ = deep_sleep_wait => { let pid = ctx.data().pid(); let tid = ctx.data().tid(); + + let thread = ctx.data().thread.clone(); + thread.set_deep_sleeping(true); + ctx.data().process.inner.1.notify_all(); + tracing::trace!(%pid, %tid, "thread entering deep sleep"); deep_sleep::(ctx, Box::pin(async move { let result = trigger.await; + tracing::trace!(%pid, %tid, "thread leaving deep sleep"); + thread.set_deep_sleeping(false); bincode::serialize(&result).unwrap().into() }))?; AsyncifyAction::Unwind @@ -490,6 +503,8 @@ where T: 'static, Fut: Future>, { + let snapshot_wait = wait_for_snapshot(env); + // This poller will process any signals when the main working function is idle struct Poller<'a, Fut, T> where @@ -497,6 +512,7 @@ where { env: &'a WasiEnv, pinned_work: Pin>, + pinned_snapshot: Pin>>, } impl<'a, Fut, T> Future for Poller<'a, Fut, T> where @@ -507,6 +523,9 @@ where if let Poll::Ready(res) = Pin::new(&mut self.pinned_work).poll(cx) { return Poll::Ready(Ok(res)); } + if let Poll::Ready(()) = Pin::new(&mut self.pinned_snapshot).poll(cx) { + return Poll::Ready(Ok(Err(Errno::Intr))); + } if let Some(exit_code) = self.env.should_exit() { return Poll::Ready(Err(WasiError::Exit(exit_code))); } @@ -960,13 +979,44 @@ pub(crate) fn deep_sleep( // Perform the unwind action let tasks = ctx.data().tasks().clone(); - let res = unwind::(ctx, move |_ctx, memory_stack, rewind_stack| { + let res = unwind::(ctx, move |mut ctx, memory_stack, rewind_stack| { + let memory_stack = memory_stack.freeze(); + let rewind_stack = rewind_stack.freeze(); + + // If journal'ing is enabled then we dump the stack into the journal + if ctx.data().enable_journal { + // Grab all the globals and serialize them + let store_data = crate::utils::store::capture_store_snapshot(&mut ctx.as_store_mut()) + .serialize() + .unwrap(); + let store_data = Bytes::from(store_data); + + tracing::debug!( + "stack snapshot unwind (memory_stack={}, rewind_stack={}, store_data={})", + memory_stack.len(), + rewind_stack.len(), + store_data.len(), + ); + + // Write our thread state to the snapshot + let tid = ctx.data().thread.tid(); + if let Err(err) = JournalEffector::save_thread_state::( + &mut ctx, + tid, + memory_stack.clone(), + rewind_stack.clone(), + store_data.clone(), + ) { + return wasmer_types::OnCalledAction::Trap(err.into()); + } + } + // Schedule the process on the stack so that it can be resumed OnCalledAction::Trap(Box::new(WasiError::DeepSleep(DeepSleepWork { trigger, rewind: RewindState { - memory_stack: memory_stack.freeze(), - rewind_stack: rewind_stack.freeze(), + memory_stack, + rewind_stack, store_data, is_64bit: M::is_64bit(), }, diff --git a/lib/wasix/src/syscalls/wasi/poll_oneoff.rs b/lib/wasix/src/syscalls/wasi/poll_oneoff.rs index a4957a49d0e..96c08189ef3 100644 --- a/lib/wasix/src/syscalls/wasi/poll_oneoff.rs +++ b/lib/wasix/src/syscalls/wasi/poll_oneoff.rs @@ -441,7 +441,6 @@ where // We use asyncify with a deep sleep to wait on new IO events let res = __asyncify_with_deep_sleep::, Errno>, _>( ctx, - Duration::from_millis(50), Box::pin(trigger), )?; if let AsyncifyAction::Finish(mut ctx, events) = res { diff --git a/lib/wasix/src/syscalls/wasix/epoll_wait.rs b/lib/wasix/src/syscalls/wasix/epoll_wait.rs index c3ea56dea51..c9c88d2a749 100644 --- a/lib/wasix/src/syscalls/wasix/epoll_wait.rs +++ b/lib/wasix/src/syscalls/wasix/epoll_wait.rs @@ -203,7 +203,6 @@ pub fn epoll_wait<'a, M: MemorySize + 'static>( // We use asyncify with a deep sleep to wait on new IO events let res = __asyncify_with_deep_sleep::, Errno>, _>( ctx, - Duration::from_millis(50), Box::pin(trigger), )?; if let AsyncifyAction::Finish(mut ctx, events) = res { diff --git a/lib/wasix/src/syscalls/wasix/futex_wait.rs b/lib/wasix/src/syscalls/wasix/futex_wait.rs index 241d026d8dc..6b72256d302 100644 --- a/lib/wasix/src/syscalls/wasix/futex_wait.rs +++ b/lib/wasix/src/syscalls/wasix/futex_wait.rs @@ -161,8 +161,7 @@ pub(super) fn futex_wait_internal( // We use asyncify on the poller and potentially go into deep sleep tracing::trace!("wait on {futex_idx}"); - let res = - __asyncify_with_deep_sleep::(ctx, Duration::from_millis(50), Box::pin(poller))?; + let res = __asyncify_with_deep_sleep::(ctx, Box::pin(poller))?; if let AsyncifyAction::Finish(ctx, res) = res { let mut env = ctx.data(); let memory = unsafe { env.memory_view(&ctx) }; diff --git a/lib/wasix/src/syscalls/wasix/proc_exec.rs b/lib/wasix/src/syscalls/wasix/proc_exec.rs index 5e3b1d93215..8e2e71f0c51 100644 --- a/lib/wasix/src/syscalls/wasix/proc_exec.rs +++ b/lib/wasix/src/syscalls/wasix/proc_exec.rs @@ -224,17 +224,13 @@ pub fn proc_exec( let thread = env.thread.clone(); // The poller will wait for the process to actually finish - let res = __asyncify_with_deep_sleep::( - ctx, - Duration::from_millis(50), - async move { - process - .wait_finished() - .await - .unwrap_or_else(|_| Errno::Child.into()) - .to_native() - }, - )?; + let res = __asyncify_with_deep_sleep::(ctx, async move { + process + .wait_finished() + .await + .unwrap_or_else(|_| Errno::Child.into()) + .to_native() + })?; match res { AsyncifyAction::Finish(mut ctx, result) => { // When we arrive here the process should already be terminated diff --git a/lib/wasix/src/syscalls/wasix/proc_join.rs b/lib/wasix/src/syscalls/wasix/proc_join.rs index a8b29b53344..3c646cbc36f 100644 --- a/lib/wasix/src/syscalls/wasix/proc_join.rs +++ b/lib/wasix/src/syscalls/wasix/proc_join.rs @@ -121,28 +121,24 @@ pub(super) fn proc_join_internal( // We wait for any process to exit (if it takes too long // then we go into a deep sleep) - let res = __asyncify_with_deep_sleep::( - ctx, - Duration::from_millis(50), - async move { - let child_exit = process.join_any_child().await; - match child_exit { - Ok(Some((pid, exit_code))) => { - tracing::trace!(%pid, %exit_code, "triggered child join"); - trace!(ret_id = pid.raw(), exit_code = exit_code.raw()); - JoinStatusResult::ExitNormal(pid, exit_code) - } - Ok(None) => { - tracing::trace!("triggered child join (no child)"); - JoinStatusResult::Err(Errno::Child) - } - Err(err) => { - tracing::trace!(%err, "error triggered on child join"); - JoinStatusResult::Err(err) - } + let res = __asyncify_with_deep_sleep::(ctx, async move { + let child_exit = process.join_any_child().await; + match child_exit { + Ok(Some((pid, exit_code))) => { + tracing::trace!(%pid, %exit_code, "triggered child join"); + trace!(ret_id = pid.raw(), exit_code = exit_code.raw()); + JoinStatusResult::ExitNormal(pid, exit_code) } - }, - )?; + Ok(None) => { + tracing::trace!("triggered child join (no child)"); + JoinStatusResult::Err(Errno::Child) + } + Err(err) => { + tracing::trace!(%err, "error triggered on child join"); + JoinStatusResult::Err(err) + } + } + })?; return match res { AsyncifyAction::Finish(ctx, result) => ret_result(ctx, result), AsyncifyAction::Unwind => Ok(Errno::Success), @@ -194,15 +190,11 @@ pub(super) fn proc_join_internal( } else { // Wait for the process to finish let process2 = process.clone(); - let res = __asyncify_with_deep_sleep::( - ctx, - Duration::from_millis(50), - async move { - let exit_code = process.join().await.unwrap_or_else(|_| Errno::Child.into()); - tracing::trace!(%exit_code, "triggered child join"); - JoinStatusResult::ExitNormal(pid, exit_code) - }, - )?; + let res = __asyncify_with_deep_sleep::(ctx, async move { + let exit_code = process.join().await.unwrap_or_else(|_| Errno::Child.into()); + tracing::trace!(%exit_code, "triggered child join"); + JoinStatusResult::ExitNormal(pid, exit_code) + })?; match res { AsyncifyAction::Finish(ctx, result) => ret_result(ctx, result), AsyncifyAction::Unwind => Ok(Errno::Success), diff --git a/lib/wasix/src/syscalls/wasix/thread_join.rs b/lib/wasix/src/syscalls/wasix/thread_join.rs index 2be67a229a3..9b9c87816b1 100644 --- a/lib/wasix/src/syscalls/wasix/thread_join.rs +++ b/lib/wasix/src/syscalls/wasix/thread_join.rs @@ -33,18 +33,17 @@ pub(super) fn thread_join_internal( let tid: WasiThreadId = join_tid.into(); let other_thread = env.process.get_thread(&tid); if let Some(other_thread) = other_thread { - let res = - __asyncify_with_deep_sleep::(ctx, Duration::from_millis(50), async move { - other_thread - .join() - .await - .map_err(|err| { - err.as_exit_code() - .unwrap_or(ExitCode::Errno(Errno::Unknown)) - }) - .unwrap_or_else(|a| a) - .raw() - })?; + let res = __asyncify_with_deep_sleep::(ctx, async move { + other_thread + .join() + .await + .map_err(|err| { + err.as_exit_code() + .unwrap_or(ExitCode::Errno(Errno::Unknown)) + }) + .unwrap_or_else(|a| a) + .raw() + })?; Ok(Errno::Success) } else { Ok(Errno::Success) diff --git a/lib/wasix/src/syscalls/wasix/thread_sleep.rs b/lib/wasix/src/syscalls/wasix/thread_sleep.rs index 1322fc640f2..41a52247bac 100644 --- a/lib/wasix/src/syscalls/wasix/thread_sleep.rs +++ b/lib/wasix/src/syscalls/wasix/thread_sleep.rs @@ -39,10 +39,9 @@ pub(crate) fn thread_sleep_internal( if duration > 0 { let duration = Duration::from_nanos(duration); let tasks = env.tasks().clone(); - let res = - __asyncify_with_deep_sleep::(ctx, Duration::from_millis(50), async move { - tasks.sleep_now(duration).await; - })?; + let res = __asyncify_with_deep_sleep::(ctx, async move { + tasks.sleep_now(duration).await; + })?; } Ok(Errno::Success) } From a4675365999c5fea656f222da6781da187f4e5b3 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Tue, 6 Feb 2024 21:49:17 +1100 Subject: [PATCH 29/67] The system now snapshots when a workload goes idle --- lib/wasix/src/syscalls/journal.rs | 43 +++++++++++++++++++++++++++++++ lib/wasix/src/syscalls/mod.rs | 32 +++++++++++++++++++++-- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/lib/wasix/src/syscalls/journal.rs b/lib/wasix/src/syscalls/journal.rs index 8213e3cb1e9..544a1938361 100644 --- a/lib/wasix/src/syscalls/journal.rs +++ b/lib/wasix/src/syscalls/journal.rs @@ -43,6 +43,49 @@ pub fn maybe_snapshot_once<'a, M: MemorySize>( Ok(Ok(ctx)) } +#[allow(clippy::extra_unused_type_parameters)] +#[cfg(not(feature = "journal"))] +pub fn maybe_snapshot_many<'a, M: MemorySize>( + ctx: FunctionEnvMut<'a, WasiEnv>, + _trigger: crate::journal::SnapshotTrigger, +) -> WasiResult> { + Ok(Ok(ctx)) +} + +#[cfg(feature = "journal")] +pub fn maybe_snapshot_many<'a, M: MemorySize>( + mut ctx: FunctionEnvMut<'a, WasiEnv>, + trigger: crate::journal::SnapshotTrigger, +) -> WasiResult> { + use crate::os::task::process::{WasiProcessCheckpoint, WasiProcessInner}; + + if unsafe { handle_rewind_ext_with_default::(&mut ctx, HandleRewindType::ResultLess) } + .is_some() + { + return Ok(Ok(ctx)); + } + + if !ctx.data().enable_journal { + return Ok(Ok(ctx)); + } + + if ctx.data_mut().has_snapshot_trigger(trigger) { + let inner = ctx.data().process.inner.clone(); + let res = wasi_try_ok_ok!(WasiProcessInner::checkpoint::( + inner, + ctx, + WasiProcessCheckpoint::Snapshot { trigger }, + )?); + match res { + MaybeCheckpointResult::Unwinding => return Ok(Err(Errno::Success)), + MaybeCheckpointResult::NotThisTime(c) => { + ctx = c; + } + } + } + Ok(Ok(ctx)) +} + #[allow(clippy::extra_unused_type_parameters)] #[cfg(not(feature = "journal"))] pub fn maybe_snapshot<'a, M: MemorySize>( diff --git a/lib/wasix/src/syscalls/mod.rs b/lib/wasix/src/syscalls/mod.rs index 4d1d916036c..347c022b2ef 100644 --- a/lib/wasix/src/syscalls/mod.rs +++ b/lib/wasix/src/syscalls/mod.rs @@ -28,6 +28,7 @@ use futures::{ use tracing::instrument; pub use wasi::*; pub use wasix::*; +use wasmer_journal::SnapshotTrigger; pub mod legacy; @@ -120,7 +121,10 @@ use crate::{ MAX_SYMLINKS, }, journal::{DynJournal, JournalEffector}, - os::task::{process::MaybeCheckpointResult, thread::RewindResult}, + os::task::{ + process::{MaybeCheckpointResult, WasiProcessCheckpoint}, + thread::RewindResult, + }, runtime::task_manager::InlineWaker, utils::store::StoreSnapshot, DeepSleepWork, RewindPostProcess, RewindState, RewindStateOption, SpawnError, WasiInodes, @@ -469,12 +473,16 @@ where let pid = ctx.data().pid(); let tid = ctx.data().tid(); + // We put thread into a deep sleeping state and + // notify anyone who is waiting for that let thread = ctx.data().thread.clone(); thread.set_deep_sleeping(true); - ctx.data().process.inner.1.notify_all(); + ctx.data().process.inner.1.notify_one(); tracing::trace!(%pid, %tid, "thread entering deep sleep"); deep_sleep::(ctx, Box::pin(async move { + // After this wakes the background work or waking + // event has triggered and its time to result let result = trigger.await; tracing::trace!(%pid, %tid, "thread leaving deep sleep"); thread.set_deep_sleeping(false); @@ -1009,6 +1017,26 @@ pub(crate) fn deep_sleep( ) { return wasmer_types::OnCalledAction::Trap(err.into()); } + + // If all the threads are now in a deep sleep state + // then we can trigger the idle snapshot event + let inner = ctx.data().process.inner.clone(); + let is_idle = { + let mut guard = inner.0.lock().unwrap(); + guard.threads.values().all(WasiThread::is_deep_sleeping) + }; + if is_idle { + if ctx.data_mut().has_snapshot_trigger(SnapshotTrigger::Idle) { + let mut guard = inner.0.lock().unwrap(); + if let Err(err) = JournalEffector::save_memory_and_snapshot( + &mut ctx, + &mut guard, + SnapshotTrigger::Idle, + ) { + return wasmer_types::OnCalledAction::Trap(err.into()); + } + } + } } // Schedule the process on the stack so that it can be resumed From 71000db46b5b931d8b7049293456e5da3215ccbe Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Tue, 6 Feb 2024 22:24:30 +1100 Subject: [PATCH 30/67] Optimized the memory snapshots to reduce wastage --- Cargo.lock | 1 + lib/wasix/Cargo.toml | 1 + .../journal/effector/memory_and_snapshot.rs | 39 +++++++++++++++++-- lib/wasix/src/os/task/process.rs | 4 ++ 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4381605bac2..96f85e30918 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6495,6 +6495,7 @@ dependencies = [ "async-trait", "base64", "bincode", + "blake3", "bytecheck", "bytes", "cfg-if", diff --git a/lib/wasix/Cargo.toml b/lib/wasix/Cargo.toml index 1eeba2e6abf..31c50636b3a 100644 --- a/lib/wasix/Cargo.toml +++ b/lib/wasix/Cargo.toml @@ -81,6 +81,7 @@ tower = { version = "0.4.13", features = ["make", "util"], optional = true } url = "2.3.1" rkyv = { workspace = true } bytecheck = "0.6.8" +blake3 = "1.0" shared-buffer = { workspace = true } petgraph = "0.6.3" base64 = "0.21" diff --git a/lib/wasix/src/journal/effector/memory_and_snapshot.rs b/lib/wasix/src/journal/effector/memory_and_snapshot.rs index a53ec4465d2..98177ee6010 100644 --- a/lib/wasix/src/journal/effector/memory_and_snapshot.rs +++ b/lib/wasix/src/journal/effector/memory_and_snapshot.rs @@ -1,9 +1,11 @@ +use std::collections::hash_map; + use super::*; impl JournalEffector { pub fn save_memory_and_snapshot( ctx: &mut FunctionEnvMut<'_, WasiEnv>, - process: &mut MutexGuard<'_, WasiProcessInner>, + guard: &mut MutexGuard<'_, WasiProcessInner>, trigger: SnapshotTrigger, ) -> anyhow::Result<()> { let env = ctx.data(); @@ -15,12 +17,14 @@ impl JournalEffector { // We do not want the regions to be greater than 64KB as this will // otherwise create too much inefficiency. We choose 64KB as its // aligned with the standard WASM page size. + let batch = 8192; let mut cur = 0u64; let mut regions = LinkedList::>::new(); while cur < memory.data_size() { let mut again = false; - let mut end = memory.data_size().min(cur + 65536); - for (_, thread) in process.threads.iter() { + let next = ((cur + batch) / batch) * batch; + let mut end = memory.data_size().min(next); + for (_, thread) in guard.threads.iter() { let layout = thread.memory_layout(); if cur >= layout.stack_lower && cur < layout.stack_upper { cur = layout.stack_upper; @@ -52,6 +56,21 @@ impl JournalEffector { .copy_range_to_vec(region.clone()) .map_err(mem_error_to_wasi)?; + // Compute a checksum and skip the memory if its already + // been saved to the journal once already + let hash: [u8; 32] = blake3::hash(&data).into(); + match guard.snapshot_memory_hash.entry(region.clone()) { + hash_map::Entry::Occupied(mut val) => { + if *val.get() == hash { + continue; + } + val.insert(hash); + } + hash_map::Entry::Vacant(vacant) => { + vacant.insert(hash); + } + } + // Now we write it to the snap snapshot capturer journal .write(JournalEntry::UpdateMemoryRegionV1 { @@ -85,10 +104,22 @@ impl JournalEffector { let memory = unsafe { env.memory() }; memory.grow_at_least(&mut store, region.end + data.len() as u64)?; + // Write the data to the memory let memory = unsafe { env.memory_view(&store) }; memory - .write(region.start, data.as_ref()) + .write(region.start, data) .map_err(|err| WasiRuntimeError::Runtime(RuntimeError::user(err.into())))?; + + // Compute the hash and update it + let hash: [u8; 32] = blake3::hash(data).into(); + env.process + .inner + .0 + .lock() + .unwrap() + .snapshot_memory_hash + .insert(region, hash); + Ok(()) } } diff --git a/lib/wasix/src/os/task/process.rs b/lib/wasix/src/os/task/process.rs index 6e627022346..eb4c368ef7c 100644 --- a/lib/wasix/src/os/task/process.rs +++ b/lib/wasix/src/os/task/process.rs @@ -136,6 +136,9 @@ pub struct WasiProcessInner { pub checkpoint: WasiProcessCheckpoint, /// Any wakers waiting on this process (for example for a checkpoint) pub wakers: Vec, + /// The snapshot memory significantly reduce the amount of + /// duplicate entries in the journal for memory that has not changed + pub snapshot_memory_hash: HashMap, [u8; 32]>, } pub enum MaybeCheckpointResult<'a> { @@ -309,6 +312,7 @@ impl WasiProcess { children: Default::default(), checkpoint: WasiProcessCheckpoint::Execute, wakers: Default::default(), + snapshot_memory_hash: Default::default(), }), Condvar::new(), )), From 3b2ec3ab53d2e1d6bf6105403ea6489a1371921a Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Wed, 7 Feb 2024 10:34:02 +1100 Subject: [PATCH 31/67] The memory snapshot functionality is now much faster using region combinations, zero copy on hashing and an optimized extent resolution --- .../journal/effector/memory_and_snapshot.rs | 127 +++++++++++++++--- lib/wasix/src/journal/effector/mod.rs | 4 +- lib/wasix/src/os/task/process.rs | 25 +++- 3 files changed, 133 insertions(+), 23 deletions(-) diff --git a/lib/wasix/src/journal/effector/memory_and_snapshot.rs b/lib/wasix/src/journal/effector/memory_and_snapshot.rs index 98177ee6010..1e6f21d66a5 100644 --- a/lib/wasix/src/journal/effector/memory_and_snapshot.rs +++ b/lib/wasix/src/journal/effector/memory_and_snapshot.rs @@ -1,7 +1,34 @@ -use std::collections::hash_map; +use std::collections::{hash_map, BTreeMap}; + +use crate::os::task::process::MemorySnapshotRegion; use super::*; +/// This value is tweaked to minimize the amount of journal +/// entries for a nominal workload but keep the resolution +/// high enough that it reduces overhead and inefficiency. +/// +/// The test case used to tune this value was a HTTP server +/// serving a HTTP web page on hyper compiled to WASM. The +/// server was first warmed up with a bunch of requests then +/// the journal entries measured on subsequent requests, these +/// are the values +/// +/// Resolution | Journal Size | Memory Overhead +/// -----------|--------------|---------------- +/// 128 bytes | 3584 bytes | 12.5% +/// 256 bytes | 4096 bytes | 6.25% +/// 512 bytes | 7680 bytes | 3.12% +/// 1024 bytes | 12288 bytes | 1.56% +/// 2048 bytes | 22528 bytes | 0.78% +/// 4096 bytes | 32769 bytes | 0.39% +/// +/// Based on this data we have settled on 512 byte memory resolution +/// for region extents which keeps the journal size to a reasonable +/// value and the memory overhead of the hash table within an acceptable +/// limit +const MEMORY_REGION_RESOLUTION: u64 = 512; + impl JournalEffector { pub fn save_memory_and_snapshot( ctx: &mut FunctionEnvMut<'_, WasiEnv>, @@ -17,12 +44,12 @@ impl JournalEffector { // We do not want the regions to be greater than 64KB as this will // otherwise create too much inefficiency. We choose 64KB as its // aligned with the standard WASM page size. - let batch = 8192; let mut cur = 0u64; - let mut regions = LinkedList::>::new(); + let mut regions = Vec::::new(); while cur < memory.data_size() { let mut again = false; - let next = ((cur + batch) / batch) * batch; + let next = ((cur + MEMORY_REGION_RESOLUTION) / MEMORY_REGION_RESOLUTION) + * MEMORY_REGION_RESOLUTION; let mut end = memory.data_size().min(next); for (_, thread) in guard.threads.iter() { let layout = thread.memory_layout(); @@ -38,28 +65,55 @@ impl JournalEffector { if again { continue; } - regions.push_back(cur..end); + + let region = cur..end; + regions.push(region.into()); cur = end; } + // Next we examine the dirty page manager and filter out any pages + // that have not been explicitly written to (according to the + // PTE) + // Now that we know all the regions that need to be saved we // enter a processing loop that dumps all the data to the log // file in an orderly manner. let memory = unsafe { env.memory_view(ctx) }; let journal = ctx.data().active_journal()?; - for region in regions { + let mut regions_phase2 = BTreeMap::new(); + for region in regions.drain(..) { // We grab this region of memory as a vector and hash // it, which allows us to make some logging efficiency // gains. + #[cfg(not(feature = "sys"))] let data = memory - .copy_range_to_vec(region.clone()) + .copy_range_to_vec(region.into()) .map_err(mem_error_to_wasi)?; + // For x86 implementations running natively we have a + // performance optimization that avoids a copy of the + // memory when hashing for changed regions + #[cfg(feature = "sys")] + let data = { + let d = unsafe { memory.data_unchecked() }; + if region.end > d.len() as u64 { + return Err(anyhow::anyhow!( + "memory access out of bounds ({} vs {})", + region.end, + d.len() + )); + } + &d[region.start as usize..region.end as usize] + }; + // Compute a checksum and skip the memory if its already // been saved to the journal once already - let hash: [u8; 32] = blake3::hash(&data).into(); - match guard.snapshot_memory_hash.entry(region.clone()) { + let hash = { + let h: [u8; 32] = blake3::hash(&data).into(); + u64::from_be_bytes([h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]]) + }; + match guard.snapshot_memory_hash.entry(region) { hash_map::Entry::Occupied(mut val) => { if *val.get() == hash { continue; @@ -71,10 +125,34 @@ impl JournalEffector { } } + regions_phase2.insert(region, ()); + } + + // Combine regions together that are next to each other + regions.clear(); + let mut last_end = None; + for (region, _) in regions_phase2.iter() { + if Some(region.start) == last_end { + regions.last_mut().unwrap().end = region.end; + } else { + regions.push(*region); + } + last_end = Some(region.end); + } + + // Perform the writes + for region in regions { + // We grab this region of memory as a vector and hash + // it, which allows us to make some logging efficiency + // gains. + let data = memory + .copy_range_to_vec(region.into()) + .map_err(mem_error_to_wasi)?; + // Now we write it to the snap snapshot capturer journal .write(JournalEntry::UpdateMemoryRegionV1 { - region, + region: region.into(), data: data.into(), }) .map_err(map_snapshot_err)?; @@ -110,15 +188,26 @@ impl JournalEffector { .write(region.start, data) .map_err(|err| WasiRuntimeError::Runtime(RuntimeError::user(err.into())))?; - // Compute the hash and update it - let hash: [u8; 32] = blake3::hash(data).into(); - env.process - .inner - .0 - .lock() - .unwrap() - .snapshot_memory_hash - .insert(region, hash); + // Break the region down into chunks that align with the resolution + let mut offset = region.start; + while offset < region.end { + let next = region.end.min(offset + MEMORY_REGION_RESOLUTION); + let region = offset..next; + offset = next; + + // Compute the hash and update it + let hash = { + let h: [u8; 32] = blake3::hash(&data).into(); + u64::from_be_bytes([h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]]) + }; + env.process + .inner + .0 + .lock() + .unwrap() + .snapshot_memory_hash + .insert(region.into(), hash); + } Ok(()) } diff --git a/lib/wasix/src/journal/effector/mod.rs b/lib/wasix/src/journal/effector/mod.rs index bf9e464da14..632207a8adb 100644 --- a/lib/wasix/src/journal/effector/mod.rs +++ b/lib/wasix/src/journal/effector/mod.rs @@ -1,6 +1,4 @@ -pub(super) use std::{ - borrow::Cow, collections::LinkedList, ops::Range, sync::MutexGuard, time::SystemTime, -}; +pub(super) use std::{borrow::Cow, ops::Range, sync::MutexGuard, time::SystemTime}; pub(super) use anyhow::bail; pub(super) use bytes::Bytes; diff --git a/lib/wasix/src/os/task/process.rs b/lib/wasix/src/os/task/process.rs index eb4c368ef7c..858d7de071e 100644 --- a/lib/wasix/src/os/task/process.rs +++ b/lib/wasix/src/os/task/process.rs @@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, convert::TryInto, + ops::Range, sync::{ atomic::{AtomicU32, Ordering}, Arc, Condvar, Mutex, MutexGuard, RwLock, Weak, @@ -118,6 +119,28 @@ pub enum WasiProcessCheckpoint { Snapshot { trigger: SnapshotTrigger }, } +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct MemorySnapshotRegion { + pub start: u64, + pub end: u64, +} + +impl From> for MemorySnapshotRegion { + fn from(value: Range) -> Self { + Self { + start: value.start, + end: value.end, + } + } +} + +impl Into> for MemorySnapshotRegion { + fn into(self) -> Range { + self.start..self.end + } +} + // TODO: fields should be private and only accessed via methods. #[derive(Debug)] pub struct WasiProcessInner { @@ -138,7 +161,7 @@ pub struct WasiProcessInner { pub wakers: Vec, /// The snapshot memory significantly reduce the amount of /// duplicate entries in the journal for memory that has not changed - pub snapshot_memory_hash: HashMap, [u8; 32]>, + pub snapshot_memory_hash: HashMap, } pub enum MaybeCheckpointResult<'a> { From 967b83a9db81e874870471d5e319613cb397b3db Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Wed, 7 Feb 2024 10:35:09 +1100 Subject: [PATCH 32/67] Added a todo on using dirty pages to optimize the hashing time when snapshotting --- lib/wasix/src/journal/effector/memory_and_snapshot.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/wasix/src/journal/effector/memory_and_snapshot.rs b/lib/wasix/src/journal/effector/memory_and_snapshot.rs index 1e6f21d66a5..8099a32b22b 100644 --- a/lib/wasix/src/journal/effector/memory_and_snapshot.rs +++ b/lib/wasix/src/journal/effector/memory_and_snapshot.rs @@ -74,6 +74,9 @@ impl JournalEffector { // Next we examine the dirty page manager and filter out any pages // that have not been explicitly written to (according to the // PTE) + // + // # TODO + // https://docs.kernel.org/admin-guide/mm/soft-dirty.html // Now that we know all the regions that need to be saved we // enter a processing loop that dumps all the data to the log From 36abb81a6030f832f2774ae8c1a46c027d906337 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Wed, 7 Feb 2024 22:56:16 +1100 Subject: [PATCH 33/67] Fixed the remote sockets --- lib/journal/src/concrete/archived.rs | 18 +- lib/journal/src/concrete/archived_from.rs | 8 +- lib/journal/src/concrete/printing.rs | 17 +- lib/journal/src/concrete/tests.rs | 4 +- lib/journal/src/entry.rs | 16 +- lib/wasix/src/bin_factory/exec.rs | 1 + .../journal/effector/syscalls/sock_accept.rs | 32 ++- .../journal/effector/syscalls/sock_connect.rs | 43 +++- lib/wasix/src/net/socket.rs | 189 ++++++++++++++++-- lib/wasix/src/state/run.rs | 1 + lib/wasix/src/syscalls/journal.rs | 32 +-- lib/wasix/src/syscalls/wasix/sock_accept.rs | 36 ++-- lib/wasix/src/syscalls/wasix/sock_connect.rs | 22 +- 13 files changed, 354 insertions(+), 65 deletions(-) diff --git a/lib/journal/src/concrete/archived.rs b/lib/journal/src/concrete/archived.rs index b7ddb28bfa8..f77b0acb443 100644 --- a/lib/journal/src/concrete/archived.rs +++ b/lib/journal/src/concrete/archived.rs @@ -720,18 +720,26 @@ impl<'a> JournalEntry<'a> { JournalEntry::SocketBindV1 { fd, addr } => { serializer.serialize_value(&JournalEntrySocketBindV1 { fd, addr }) } - JournalEntry::SocketConnectedV1 { fd, addr } => { - serializer.serialize_value(&JournalEntrySocketConnectedV1 { fd, addr }) - } + JournalEntry::SocketConnectedV1 { + fd, + local_addr, + peer_addr, + } => serializer.serialize_value(&JournalEntrySocketConnectedV1 { + fd, + local_addr, + peer_addr, + }), JournalEntry::SocketAcceptedV1 { listen_fd, fd, + local_addr: addr, peer_addr, fd_flags, non_blocking: nonblocking, } => serializer.serialize_value(&JournalEntrySocketAcceptedV1 { listen_fd, fd, + local_addr: addr, peer_addr, fd_flags: fd_flags.bits(), nonblocking, @@ -1330,7 +1338,8 @@ pub struct JournalEntrySocketBindV1 { #[archive_attr(derive(CheckBytes), repr(align(8)))] pub struct JournalEntrySocketConnectedV1 { pub fd: u32, - pub addr: SocketAddr, + pub local_addr: SocketAddr, + pub peer_addr: SocketAddr, } #[repr(C)] @@ -1340,6 +1349,7 @@ pub struct JournalEntrySocketConnectedV1 { pub struct JournalEntrySocketAcceptedV1 { pub listen_fd: u32, pub fd: u32, + pub local_addr: SocketAddr, pub peer_addr: SocketAddr, pub fd_flags: u16, pub nonblocking: bool, diff --git a/lib/journal/src/concrete/archived_from.rs b/lib/journal/src/concrete/archived_from.rs index aaf68faeb9d..93e59f66cce 100644 --- a/lib/journal/src/concrete/archived_from.rs +++ b/lib/journal/src/concrete/archived_from.rs @@ -945,20 +945,24 @@ impl<'a> TryFrom> for JournalEntry<'a> { } ArchivedJournalEntry::SocketConnectedV1(ArchivedJournalEntrySocketConnectedV1 { fd, - addr, + local_addr, + peer_addr, }) => Self::SocketConnectedV1 { fd: *fd, - addr: addr.as_socket_addr(), + local_addr: local_addr.as_socket_addr(), + peer_addr: peer_addr.as_socket_addr(), }, ArchivedJournalEntry::SocketAcceptedV1(ArchivedJournalEntrySocketAcceptedV1 { listen_fd, fd, + local_addr, peer_addr, fd_flags, nonblocking, }) => Self::SocketAcceptedV1 { listen_fd: *listen_fd, fd: *fd, + local_addr: local_addr.as_socket_addr(), peer_addr: peer_addr.as_socket_addr(), fd_flags: wasi::Fdflags::from_bits_truncate(*fd_flags), non_blocking: *nonblocking, diff --git a/lib/journal/src/concrete/printing.rs b/lib/journal/src/concrete/printing.rs index 168eab11264..2e6378a0424 100644 --- a/lib/journal/src/concrete/printing.rs +++ b/lib/journal/src/concrete/printing.rs @@ -249,18 +249,27 @@ impl<'a> fmt::Display for JournalEntry<'a> { JournalEntry::SocketBindV1 { fd, addr } => { write!(f, "sock-bind (fd={}, addr={})", fd, addr) } - JournalEntry::SocketConnectedV1 { fd, addr } => { - write!(f, "sock-connect (fd={}, addr={})", fd, addr) + JournalEntry::SocketConnectedV1 { + fd, + local_addr, + peer_addr, + } => { + write!( + f, + "sock-connect (fd={}, addr={}, peer={})", + fd, local_addr, peer_addr + ) } JournalEntry::SocketAcceptedV1 { listen_fd, fd, + local_addr, peer_addr, .. } => write!( f, - "sock-accept (listen-fd={}, sock_fd={}, peer={})", - listen_fd, fd, peer_addr + "sock-accept (listen-fd={}, sock_fd={}, addr={}, peer={})", + listen_fd, fd, local_addr, peer_addr ), JournalEntry::SocketJoinIpv4MulticastV1 { fd, diff --git a/lib/journal/src/concrete/tests.rs b/lib/journal/src/concrete/tests.rs index cc48dedb4af..a987069d21e 100644 --- a/lib/journal/src/concrete/tests.rs +++ b/lib/journal/src/concrete/tests.rs @@ -481,7 +481,8 @@ pub fn test_record_socket_bind() { pub fn test_record_socket_connected() { run_test(JournalEntry::SocketConnectedV1 { fd: 12341, - addr: SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 1234), + local_addr: SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 1234), + peer_addr: SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 1234), }); } @@ -491,6 +492,7 @@ pub fn test_record_socket_accepted() { run_test(JournalEntry::SocketAcceptedV1 { listen_fd: 21234, fd: 1, + local_addr: SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 3452), peer_addr: SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 3452), fd_flags: wasi::Fdflags::all(), non_blocking: true, diff --git a/lib/journal/src/entry.rs b/lib/journal/src/entry.rs index aace436d22d..2f8862f0200 100644 --- a/lib/journal/src/entry.rs +++ b/lib/journal/src/entry.rs @@ -287,11 +287,13 @@ pub enum JournalEntry<'a> { }, SocketConnectedV1 { fd: Fd, - addr: SocketAddr, + local_addr: SocketAddr, + peer_addr: SocketAddr, }, SocketAcceptedV1 { listen_fd: Fd, fd: Fd, + local_addr: SocketAddr, peer_addr: SocketAddr, fd_flags: Fdflags, non_blocking: bool, @@ -592,16 +594,26 @@ impl<'a> JournalEntry<'a> { Self::SocketOpenV1 { af, ty, pt, fd } => JournalEntry::SocketOpenV1 { af, ty, pt, fd }, Self::SocketListenV1 { fd, backlog } => JournalEntry::SocketListenV1 { fd, backlog }, Self::SocketBindV1 { fd, addr } => JournalEntry::SocketBindV1 { fd, addr }, - Self::SocketConnectedV1 { fd, addr } => JournalEntry::SocketConnectedV1 { fd, addr }, + Self::SocketConnectedV1 { + fd, + local_addr, + peer_addr, + } => JournalEntry::SocketConnectedV1 { + fd, + local_addr, + peer_addr, + }, Self::SocketAcceptedV1 { listen_fd, fd, + local_addr, peer_addr, fd_flags, non_blocking: nonblocking, } => JournalEntry::SocketAcceptedV1 { listen_fd, fd, + local_addr, peer_addr, fd_flags, non_blocking: nonblocking, diff --git a/lib/wasix/src/bin_factory/exec.rs b/lib/wasix/src/bin_factory/exec.rs index 1f82d365c1b..dfc8d02ec9b 100644 --- a/lib/wasix/src/bin_factory/exec.rs +++ b/lib/wasix/src/bin_factory/exec.rs @@ -151,6 +151,7 @@ pub fn run_exec(props: TaskWasmRunProperties) { let rewind_state = match unsafe { ctx.bootstrap(&mut store) } { Ok(r) => r, Err(err) => { + tracing::warn!("failed to bootstrap - {}", err); thread.thread.set_status_finished(Err(err)); ctx.data(&store) .blocking_on_exit(Some(Errno::Noexec.into())); diff --git a/lib/wasix/src/journal/effector/syscalls/sock_accept.rs b/lib/wasix/src/journal/effector/syscalls/sock_accept.rs index 3482074dcfe..eebb61d9423 100644 --- a/lib/wasix/src/journal/effector/syscalls/sock_accept.rs +++ b/lib/wasix/src/journal/effector/syscalls/sock_accept.rs @@ -1,5 +1,7 @@ use std::net::SocketAddr; +use wasmer_wasix_types::wasi::{Addressfamily, SockProto, Socktype}; + use crate::{ fs::Kind, net::socket::{InodeSocket, InodeSocketKind}, @@ -12,6 +14,7 @@ impl JournalEffector { ctx: &mut FunctionEnvMut<'_, WasiEnv>, listen_fd: Fd, fd: Fd, + addr: SocketAddr, peer_addr: SocketAddr, fd_flags: Fdflags, nonblocking: bool, @@ -21,6 +24,7 @@ impl JournalEffector { JournalEntry::SocketAcceptedV1 { listen_fd, fd, + local_addr: addr, peer_addr, fd_flags, non_blocking: nonblocking, @@ -32,12 +36,38 @@ impl JournalEffector { ctx: &mut FunctionEnvMut<'_, WasiEnv>, _listen_fd: Fd, fd: Fd, + addr: SocketAddr, peer_addr: SocketAddr, fd_flags: Fdflags, nonblocking: bool, ) -> anyhow::Result<()> { let kind = Kind::Socket { - socket: InodeSocket::new(InodeSocketKind::RemoteTcpStream { peer_addr }), + socket: InodeSocket::new(InodeSocketKind::RemoteSocket { + local_addr: addr, + peer_addr, + family: match peer_addr.is_ipv4() { + true => Addressfamily::Inet4, + false => Addressfamily::Inet6, + }, + ty: Socktype::Stream, + pt: SockProto::Tcp, + + only_v6: false, + reuse_port: false, + reuse_addr: false, + ttl: 0, + multicast_ttl: 0, + no_delay: None, + keep_alive: None, + dont_route: None, + send_buf_size: None, + recv_buf_size: None, + write_timeout: None, + read_timeout: None, + accept_timeout: None, + connect_timeout: None, + handler: None, + }), }; let env = ctx.data(); diff --git a/lib/wasix/src/journal/effector/syscalls/sock_connect.rs b/lib/wasix/src/journal/effector/syscalls/sock_connect.rs index 3c7af982efd..4be134f5423 100644 --- a/lib/wasix/src/journal/effector/syscalls/sock_connect.rs +++ b/lib/wasix/src/journal/effector/syscalls/sock_connect.rs @@ -1,5 +1,7 @@ use std::net::SocketAddr; +use wasmer_wasix_types::wasi::{Addressfamily, SockProto, Socktype}; + use crate::{ fs::Kind, net::socket::{InodeSocket, InodeSocketKind}, @@ -11,18 +13,51 @@ impl JournalEffector { pub fn save_sock_connect( ctx: &mut FunctionEnvMut<'_, WasiEnv>, fd: Fd, - addr: SocketAddr, + local_addr: SocketAddr, + peer_addr: SocketAddr, ) -> anyhow::Result<()> { - Self::save_event(ctx, JournalEntry::SocketConnectedV1 { fd, addr }) + Self::save_event( + ctx, + JournalEntry::SocketConnectedV1 { + fd, + local_addr, + peer_addr, + }, + ) } pub fn apply_sock_connect( ctx: &mut FunctionEnvMut<'_, WasiEnv>, fd: Fd, - addr: SocketAddr, + local_addr: SocketAddr, + peer_addr: SocketAddr, ) -> anyhow::Result<()> { let kind = Kind::Socket { - socket: InodeSocket::new(InodeSocketKind::RemoteTcpStream { peer_addr: addr }), + socket: InodeSocket::new(InodeSocketKind::RemoteSocket { + local_addr, + peer_addr, + family: match peer_addr.is_ipv4() { + true => Addressfamily::Inet4, + false => Addressfamily::Inet6, + }, + ty: Socktype::Stream, + pt: SockProto::Tcp, + only_v6: false, + reuse_port: false, + reuse_addr: false, + ttl: 0, + multicast_ttl: 0, + no_delay: None, + keep_alive: None, + dont_route: None, + send_buf_size: None, + recv_buf_size: None, + write_timeout: None, + read_timeout: None, + accept_timeout: None, + connect_timeout: None, + handler: None, + }), }; let env = ctx.data(); diff --git a/lib/wasix/src/net/socket.rs b/lib/wasix/src/net/socket.rs index 958a01578b1..813c8366427 100644 --- a/lib/wasix/src/net/socket.rs +++ b/lib/wasix/src/net/socket.rs @@ -72,8 +72,28 @@ pub enum InodeSocketKind { socket: Box, peer: Option, }, - RemoteTcpStream { + RemoteSocket { + family: Addressfamily, + ty: Socktype, + pt: SockProto, + local_addr: SocketAddr, peer_addr: SocketAddr, + only_v6: bool, + reuse_port: bool, + reuse_addr: bool, + ttl: u32, + multicast_ttl: u32, + no_delay: Option, + keep_alive: Option, + dont_route: Option, + send_buf_size: Option, + recv_buf_size: Option, + write_timeout: Option, + read_timeout: Option, + accept_timeout: Option, + connect_timeout: Option, + #[derivative(Debug = "ignore")] + handler: Option>, }, } @@ -290,6 +310,55 @@ impl InodeSocket { _ => return Err(Errno::Inval), } } + InodeSocketKind::RemoteSocket { + family, + ty, + local_addr: addr, + reuse_port, + reuse_addr, + .. + } => { + match *family { + Addressfamily::Inet4 => { + if !set_addr.is_ipv4() { + tracing::debug!( + "IP address is the wrong type IPv4 ({set_addr}) vs IPv6 family" + ); + return Err(Errno::Inval); + } + } + Addressfamily::Inet6 => { + if !set_addr.is_ipv6() { + tracing::debug!( + "IP address is the wrong type IPv6 ({set_addr}) vs IPv4 family" + ); + return Err(Errno::Inval); + } + } + _ => { + return Err(Errno::Notsup); + } + } + + *addr = set_addr; + let addr = *addr; + + match *ty { + Socktype::Stream => { + // we already set the socket address - next we need a listen or connect so nothing + // more to do at this time + return Ok(None); + } + Socktype::Dgram => { + let reuse_port = *reuse_port; + let reuse_addr = *reuse_addr; + drop(inner); + + net.bind_udp(addr, reuse_port, reuse_addr) + } + _ => return Err(Errno::Inval), + } + } _ => return Err(Errno::Notsup), } }; @@ -344,6 +413,28 @@ impl InodeSocket { return Err(Errno::Notsup); } }, + InodeSocketKind::RemoteSocket { + ty, + local_addr: addr, + only_v6, + reuse_port, + reuse_addr, + .. + } => match *ty { + Socktype::Stream => { + let addr = *addr; + let only_v6 = *only_v6; + let reuse_port = *reuse_port; + let reuse_addr = *reuse_addr; + drop(inner); + + net.listen_tcp(addr, only_v6, reuse_port, reuse_addr) + } + _ => { + tracing::warn!("wasi[?]::sock_listen - failed - not supported(1)"); + return Err(Errno::Notsup); + } + }, _ => { tracing::warn!("wasi[?]::sock_listen - failed - not supported(2)"); return Err(Errno::Notsup); @@ -441,7 +532,7 @@ impl InodeSocket { InodeSocketKind::UdpSocket { .. } => {} InodeSocketKind::Raw(_) => {} InodeSocketKind::PreSocket { .. } => return Err(Errno::Notconn), - InodeSocketKind::RemoteTcpStream { .. } => {} + InodeSocketKind::RemoteSocket { .. } => {} }; Ok(()) } @@ -515,6 +606,10 @@ impl InodeSocket { target_peer.replace(peer); return Ok(None); } + InodeSocketKind::RemoteSocket { peer_addr, .. } => { + *peer_addr = peer; + return Ok(None); + } _ => return Err(Errno::Notsup), } }; @@ -546,6 +641,7 @@ impl InodeSocket { InodeSocketKind::TcpListener { .. } => WasiSocketStatus::Opened, InodeSocketKind::TcpStream { .. } => WasiSocketStatus::Opened, InodeSocketKind::UdpSocket { .. } => WasiSocketStatus::Opened, + InodeSocketKind::RemoteSocket { .. } => WasiSocketStatus::Opened, _ => WasiSocketStatus::Failed, }) } @@ -577,6 +673,9 @@ impl InodeSocket { InodeSocketKind::UdpSocket { socket, .. } => { socket.addr_local().map_err(net_error_into_wasi_err)? } + InodeSocketKind::RemoteSocket { + local_addr: addr, .. + } => *addr, _ => return Err(Errno::Notsup), }) } @@ -613,6 +712,7 @@ impl InodeSocket { ) }) })?, + InodeSocketKind::RemoteSocket { peer_addr, .. } => *peer_addr, _ => return Err(Errno::Notsup), }) } @@ -628,6 +728,15 @@ impl InodeSocket { keep_alive, dont_route, .. + } + | InodeSocketKind::RemoteSocket { + only_v6, + reuse_port, + reuse_addr, + no_delay, + keep_alive, + dont_route, + .. } => { match option { WasiSocketOption::OnlyV6 => *only_v6 = val, @@ -685,6 +794,14 @@ impl InodeSocket { no_delay, keep_alive, .. + } + | InodeSocketKind::RemoteSocket { + only_v6, + reuse_port, + reuse_addr, + no_delay, + keep_alive, + .. } => match option { WasiSocketOption::OnlyV6 => *only_v6, WasiSocketOption::ReusePort => *reuse_port, @@ -728,7 +845,8 @@ impl InodeSocket { pub fn set_send_buf_size(&mut self, size: usize) -> Result<(), Errno> { let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { - InodeSocketKind::PreSocket { send_buf_size, .. } => { + InodeSocketKind::PreSocket { send_buf_size, .. } + | InodeSocketKind::RemoteSocket { send_buf_size, .. } => { *send_buf_size = Some(size); } InodeSocketKind::TcpStream { socket, .. } => { @@ -744,7 +862,8 @@ impl InodeSocket { pub fn send_buf_size(&self) -> Result { let inner = self.inner.protected.read().unwrap(); match &inner.kind { - InodeSocketKind::PreSocket { send_buf_size, .. } => { + InodeSocketKind::PreSocket { send_buf_size, .. } + | InodeSocketKind::RemoteSocket { send_buf_size, .. } => { Ok((*send_buf_size).unwrap_or_default()) } InodeSocketKind::TcpStream { socket, .. } => { @@ -757,7 +876,8 @@ impl InodeSocket { pub fn set_recv_buf_size(&mut self, size: usize) -> Result<(), Errno> { let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { - InodeSocketKind::PreSocket { recv_buf_size, .. } => { + InodeSocketKind::PreSocket { recv_buf_size, .. } + | InodeSocketKind::RemoteSocket { recv_buf_size, .. } => { *recv_buf_size = Some(size); } InodeSocketKind::TcpStream { socket, .. } => { @@ -773,7 +893,8 @@ impl InodeSocket { pub fn recv_buf_size(&self) -> Result { let inner = self.inner.protected.read().unwrap(); match &inner.kind { - InodeSocketKind::PreSocket { recv_buf_size, .. } => { + InodeSocketKind::PreSocket { recv_buf_size, .. } + | InodeSocketKind::RemoteSocket { recv_buf_size, .. } => { Ok((*recv_buf_size).unwrap_or_default()) } InodeSocketKind::TcpStream { socket, .. } => { @@ -789,6 +910,7 @@ impl InodeSocket { InodeSocketKind::TcpStream { socket, .. } => { socket.set_linger(linger).map_err(net_error_into_wasi_err) } + InodeSocketKind::RemoteSocket { .. } => Ok(()), InodeSocketKind::PreSocket { .. } => Err(Errno::Io), _ => Err(Errno::Notsup), } @@ -837,6 +959,13 @@ impl InodeSocket { connect_timeout, accept_timeout, .. + } + | InodeSocketKind::RemoteSocket { + read_timeout, + write_timeout, + connect_timeout, + accept_timeout, + .. } => { match ty { TimeType::ConnectTimeout => *connect_timeout = timeout, @@ -873,6 +1002,13 @@ impl InodeSocket { connect_timeout, accept_timeout, .. + } + | InodeSocketKind::RemoteSocket { + read_timeout, + write_timeout, + connect_timeout, + accept_timeout, + .. } => match ty { TimeType::ConnectTimeout => Ok(*connect_timeout), TimeType::AcceptTimeout => Ok(*accept_timeout), @@ -893,6 +1029,10 @@ impl InodeSocket { InodeSocketKind::UdpSocket { socket, .. } => { socket.set_ttl(ttl).map_err(net_error_into_wasi_err) } + InodeSocketKind::RemoteSocket { ttl: set_ttl, .. } => { + *set_ttl = ttl; + Ok(()) + } InodeSocketKind::PreSocket { .. } => Err(Errno::Io), _ => Err(Errno::Notsup), } @@ -907,6 +1047,7 @@ impl InodeSocket { InodeSocketKind::UdpSocket { socket, .. } => { socket.ttl().map_err(net_error_into_wasi_err) } + InodeSocketKind::RemoteSocket { ttl, .. } => Ok(*ttl), InodeSocketKind::PreSocket { .. } => Err(Errno::Io), _ => Err(Errno::Notsup), } @@ -918,6 +1059,13 @@ impl InodeSocket { InodeSocketKind::UdpSocket { socket, .. } => socket .set_multicast_ttl_v4(ttl) .map_err(net_error_into_wasi_err), + InodeSocketKind::RemoteSocket { + multicast_ttl: set_ttl, + .. + } => { + *set_ttl = ttl; + Ok(()) + } InodeSocketKind::PreSocket { .. } => Err(Errno::Io), _ => Err(Errno::Notsup), } @@ -929,6 +1077,7 @@ impl InodeSocket { InodeSocketKind::UdpSocket { socket, .. } => { socket.multicast_ttl_v4().map_err(net_error_into_wasi_err) } + InodeSocketKind::RemoteSocket { multicast_ttl, .. } => Ok(*multicast_ttl), InodeSocketKind::PreSocket { .. } => Err(Errno::Io), _ => Err(Errno::Notsup), } @@ -940,6 +1089,7 @@ impl InodeSocket { InodeSocketKind::UdpSocket { socket, .. } => socket .join_multicast_v4(multiaddr, iface) .map_err(net_error_into_wasi_err), + InodeSocketKind::RemoteSocket { .. } => Ok(()), InodeSocketKind::PreSocket { .. } => Err(Errno::Io), _ => Err(Errno::Notsup), } @@ -951,6 +1101,7 @@ impl InodeSocket { InodeSocketKind::UdpSocket { socket, .. } => socket .leave_multicast_v4(multiaddr, iface) .map_err(net_error_into_wasi_err), + InodeSocketKind::RemoteSocket { .. } => Ok(()), InodeSocketKind::PreSocket { .. } => Err(Errno::Io), _ => Err(Errno::Notsup), } @@ -962,6 +1113,7 @@ impl InodeSocket { InodeSocketKind::UdpSocket { socket, .. } => socket .join_multicast_v6(multiaddr, iface) .map_err(net_error_into_wasi_err), + InodeSocketKind::RemoteSocket { .. } => Ok(()), InodeSocketKind::PreSocket { .. } => Err(Errno::Io), _ => Err(Errno::Notsup), } @@ -973,6 +1125,7 @@ impl InodeSocket { InodeSocketKind::UdpSocket { socket, .. } => socket .leave_multicast_v6(multiaddr, iface) .map_err(net_error_into_wasi_err), + InodeSocketKind::RemoteSocket { .. } => Ok(()), InodeSocketKind::PreSocket { .. } => Err(Errno::Io), _ => Err(Errno::Notsup), } @@ -1020,7 +1173,7 @@ impl InodeSocket { InodeSocketKind::PreSocket { .. } => { return Poll::Ready(Err(Errno::Notconn)) } - InodeSocketKind::RemoteTcpStream { .. } => { + InodeSocketKind::RemoteSocket { .. } => { return Poll::Ready(Ok(self.data.len())) } _ => return Poll::Ready(Err(Errno::Notsup)), @@ -1100,7 +1253,7 @@ impl InodeSocket { InodeSocketKind::PreSocket { .. } => { return Poll::Ready(Err(Errno::Notconn)) } - InodeSocketKind::RemoteTcpStream { .. } => { + InodeSocketKind::RemoteSocket { .. } => { return Poll::Ready(Ok(self.data.len())) } _ => return Poll::Ready(Err(Errno::Notsup)), @@ -1188,6 +1341,9 @@ impl InodeSocket { } } } + InodeSocketKind::RemoteSocket { .. } => { + return Poll::Pending; + } InodeSocketKind::PreSocket { .. } => { return Poll::Ready(Err(Errno::Notconn)) } @@ -1264,6 +1420,9 @@ impl InodeSocket { InodeSocketKind::UdpSocket { socket, .. } => { socket.try_recv_from(self.data) } + InodeSocketKind::RemoteSocket { .. } => { + return Poll::Pending; + } InodeSocketKind::PreSocket { .. } => { return Poll::Ready(Err(Errno::Notconn)) } @@ -1310,6 +1469,7 @@ impl InodeSocket { InodeSocketKind::TcpStream { socket, .. } => { socket.shutdown(how).map_err(net_error_into_wasi_err)?; } + InodeSocketKind::RemoteSocket { .. } => return Ok(()), InodeSocketKind::PreSocket { .. } => return Err(Errno::Notconn), _ => return Err(Errno::Notsup), } @@ -1321,6 +1481,7 @@ impl InodeSocket { #[allow(clippy::match_like_matches_macro)] match &mut guard.kind { InodeSocketKind::TcpStream { .. } + | InodeSocketKind::RemoteSocket { .. } | InodeSocketKind::UdpSocket { .. } | InodeSocketKind::Raw(..) => true, _ => false, @@ -1342,7 +1503,9 @@ impl InodeSocketProtected { InodeSocketKind::PreSocket { handler, .. } => { handler.take(); } - InodeSocketKind::RemoteTcpStream { .. } => {} + InodeSocketKind::RemoteSocket { handler, .. } => { + handler.take(); + } } } @@ -1354,7 +1517,7 @@ impl InodeSocketProtected { InodeSocketKind::Raw(socket) => socket.poll_read_ready(cx), InodeSocketKind::Icmp(socket) => socket.poll_read_ready(cx), InodeSocketKind::PreSocket { .. } => Poll::Pending, - InodeSocketKind::RemoteTcpStream { .. } => Poll::Pending, + InodeSocketKind::RemoteSocket { .. } => Poll::Pending, } .map_err(net_error_into_io_err) } @@ -1367,7 +1530,7 @@ impl InodeSocketProtected { InodeSocketKind::Raw(socket) => socket.poll_write_ready(cx), InodeSocketKind::Icmp(socket) => socket.poll_write_ready(cx), InodeSocketKind::PreSocket { .. } => Poll::Pending, - InodeSocketKind::RemoteTcpStream { .. } => Poll::Pending, + InodeSocketKind::RemoteSocket { .. } => Poll::Pending, } .map_err(net_error_into_io_err) } @@ -1382,11 +1545,11 @@ impl InodeSocketProtected { InodeSocketKind::UdpSocket { socket, .. } => socket.set_handler(handler), InodeSocketKind::Raw(socket) => socket.set_handler(handler), InodeSocketKind::Icmp(socket) => socket.set_handler(handler), - InodeSocketKind::PreSocket { handler: h, .. } => { + InodeSocketKind::PreSocket { handler: h, .. } + | InodeSocketKind::RemoteSocket { handler: h, .. } => { h.replace(handler); Ok(()) } - InodeSocketKind::RemoteTcpStream { .. } => Ok(()), } } } diff --git a/lib/wasix/src/state/run.rs b/lib/wasix/src/state/run.rs index 45f450d6d5e..8f733ed4427 100644 --- a/lib/wasix/src/state/run.rs +++ b/lib/wasix/src/state/run.rs @@ -42,6 +42,7 @@ impl WasiFunctionEnv { match this.bootstrap(&mut store) { Ok(a) => a, Err(err) => { + tracing::warn!("failed to bootstrap - {}", err); this.on_exit(&mut store, None); tx.send(Err(err)).ok(); return; diff --git a/lib/wasix/src/syscalls/journal.rs b/lib/wasix/src/syscalls/journal.rs index 544a1938361..9131a7fa680 100644 --- a/lib/wasix/src/syscalls/journal.rs +++ b/lib/wasix/src/syscalls/journal.rs @@ -596,13 +596,16 @@ pub unsafe fn restore_snapshot( JournalEffector::apply_sock_bind(&mut ctx, fd, addr) .map_err(anyhow_err_to_runtime_err)? } - crate::journal::JournalEntry::SocketConnectedV1 { fd, addr } => { - JournalEffector::apply_sock_connect(&mut ctx, fd, addr) - .map_err(anyhow_err_to_runtime_err)? - } + crate::journal::JournalEntry::SocketConnectedV1 { + fd, + local_addr, + peer_addr, + } => JournalEffector::apply_sock_connect(&mut ctx, fd, local_addr, peer_addr) + .map_err(anyhow_err_to_runtime_err)?, crate::journal::JournalEntry::SocketAcceptedV1 { listen_fd, fd, + local_addr: addr, peer_addr, fd_flags, non_blocking: nonblocking, @@ -610,6 +613,7 @@ pub unsafe fn restore_snapshot( &mut ctx, listen_fd, fd, + addr, peer_addr, fd_flags, nonblocking, @@ -737,16 +741,6 @@ pub unsafe fn restore_snapshot( } } - // We do not yet support multi threading - if !spawn_threads.is_empty() { - return Err(WasiRuntimeError::Runtime(RuntimeError::user( - anyhow::format_err!( - "Snapshot restoration does not currently support multiple threads." - ) - .into(), - ))); - } - // Now output the stdout and stderr for (offset, data, is_64bit) in stdout { if is_64bit { @@ -775,5 +769,15 @@ pub unsafe fn restore_snapshot( JournalEffector::apply_tty_set(&mut ctx, state).map_err(anyhow_err_to_runtime_err)?; } + // We do not yet support multi threading + if !spawn_threads.is_empty() { + return Err(WasiRuntimeError::Runtime(RuntimeError::user( + anyhow::format_err!( + "Snapshot restoration does not currently support multiple threads." + ) + .into(), + ))); + } + Ok(rewind) } diff --git a/lib/wasix/src/syscalls/wasix/sock_accept.rs b/lib/wasix/src/syscalls/wasix/sock_accept.rs index 9ce6b75a63c..727bf85bde3 100644 --- a/lib/wasix/src/syscalls/wasix/sock_accept.rs +++ b/lib/wasix/src/syscalls/wasix/sock_accept.rs @@ -31,7 +31,7 @@ pub fn sock_accept( let nonblocking = fd_flags.contains(Fdflags::NONBLOCK); - let (fd, addr) = wasi_try_ok!(sock_accept_internal(env, sock, fd_flags, nonblocking)?); + let (fd, _, _) = wasi_try_ok!(sock_accept_internal(env, sock, fd_flags, nonblocking)?); wasi_try_mem_ok!(ro_fd.write(&memory, fd)); @@ -66,15 +66,24 @@ pub fn sock_accept_v2( let nonblocking = fd_flags.contains(Fdflags::NONBLOCK); - let (fd, addr) = wasi_try_ok!(sock_accept_internal(env, sock, fd_flags, nonblocking)?); + let (fd, local_addr, peer_addr) = + wasi_try_ok!(sock_accept_internal(env, sock, fd_flags, nonblocking)?); #[cfg(feature = "journal")] if ctx.data().enable_journal { - JournalEffector::save_sock_accepted(&mut ctx, sock, fd, addr, fd_flags, nonblocking) - .map_err(|err| { - tracing::error!("failed to save sock_accepted event - {}", err); - WasiError::Exit(ExitCode::Errno(Errno::Fault)) - })?; + JournalEffector::save_sock_accepted( + &mut ctx, + sock, + fd, + local_addr, + peer_addr, + fd_flags, + nonblocking, + ) + .map_err(|err| { + tracing::error!("failed to save sock_accepted event - {}", err); + WasiError::Exit(ExitCode::Errno(Errno::Fault)) + })?; } let env = ctx.data(); @@ -83,8 +92,8 @@ pub fn sock_accept_v2( wasi_try_ok!(crate::net::write_ip_port( &memory, ro_addr, - addr.ip(), - addr.port() + peer_addr.ip(), + peer_addr.port() )); Ok(Errno::Success) @@ -95,12 +104,12 @@ pub(crate) fn sock_accept_internal( sock: WasiFd, mut fd_flags: Fdflags, mut nonblocking: bool, -) -> Result, WasiError> { +) -> Result, WasiError> { let state = env.state(); let inodes = &state.inodes; let tasks = env.tasks().clone(); - let (child, addr, fd_flags) = wasi_try_ok_ok!(__sock_asyncify( + let (child, local_addr, peer_addr, fd_flags) = wasi_try_ok_ok!(__sock_asyncify( env, sock, Rights::SOCK_ACCEPT, @@ -114,10 +123,11 @@ pub(crate) fn sock_accept_internal( .ok() .flatten() .unwrap_or(Duration::from_secs(30)); + let local_addr = socket.addr_local()?; socket .accept(tasks.deref(), nonblocking, Some(timeout)) .await - .map(|a| (a.0, a.1, fd_flags)) + .map(|a| (a.0, local_addr, a.1, fd_flags)) }, )); @@ -146,5 +156,5 @@ pub(crate) fn sock_accept_internal( let fd = wasi_try_ok_ok!(state.fs.create_fd(rights, rights, new_flags, 0, inode)); Span::current().record("fd", fd); - Ok(Ok((fd, addr))) + Ok(Ok((fd, local_addr, peer_addr))) } diff --git a/lib/wasix/src/syscalls/wasix/sock_connect.rs b/lib/wasix/src/syscalls/wasix/sock_connect.rs index 03e51ec28b4..e935bec8627 100644 --- a/lib/wasix/src/syscalls/wasix/sock_connect.rs +++ b/lib/wasix/src/syscalls/wasix/sock_connect.rs @@ -22,17 +22,25 @@ pub fn sock_connect( let env = ctx.data(); let memory = unsafe { env.memory_view(&ctx) }; let addr = wasi_try_ok!(crate::net::read_ip_port(&memory, addr)); - let addr = SocketAddr::new(addr.0, addr.1); - Span::current().record("addr", &format!("{:?}", addr)); + let peer_addr = SocketAddr::new(addr.0, addr.1); + Span::current().record("addr", &format!("{:?}", peer_addr)); - wasi_try_ok!(sock_connect_internal(&mut ctx, sock, addr)?); + wasi_try_ok!(sock_connect_internal(&mut ctx, sock, peer_addr)?); #[cfg(feature = "journal")] if ctx.data().enable_journal { - JournalEffector::save_sock_connect(&mut ctx, sock, addr).map_err(|err| { - tracing::error!("failed to save sock_connected event - {}", err); - WasiError::Exit(ExitCode::Errno(Errno::Fault)) - })?; + let local_addr = wasi_try_ok!(__sock_actor( + &mut ctx, + sock, + Rights::empty(), + |socket, _| socket.addr_local() + )); + JournalEffector::save_sock_connect(&mut ctx, sock, local_addr, peer_addr).map_err( + |err| { + tracing::error!("failed to save sock_connected event - {}", err); + WasiError::Exit(ExitCode::Errno(Errno::Fault)) + }, + )?; } Ok(Errno::Success) From aa64c36d17c352b27d999cdeacde77858f0698e2 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Wed, 7 Feb 2024 23:11:49 +1100 Subject: [PATCH 34/67] Fixed the writing of the memory hashes to the bitmap --- .../journal/effector/memory_and_snapshot.rs | 8 +++++-- lib/wasix/src/syscalls/journal.rs | 23 +++++++++++-------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/lib/wasix/src/journal/effector/memory_and_snapshot.rs b/lib/wasix/src/journal/effector/memory_and_snapshot.rs index 8099a32b22b..860905da2cd 100644 --- a/lib/wasix/src/journal/effector/memory_and_snapshot.rs +++ b/lib/wasix/src/journal/effector/memory_and_snapshot.rs @@ -178,7 +178,7 @@ impl JournalEffector { pub unsafe fn apply_memory( ctx: &mut FunctionEnvMut<'_, WasiEnv>, region: Range, - data: &[u8], + mut data: &[u8], ) -> anyhow::Result<()> { let (env, mut store) = ctx.data_and_store_mut(); @@ -199,8 +199,9 @@ impl JournalEffector { offset = next; // Compute the hash and update it + let size = region.end - region.start; let hash = { - let h: [u8; 32] = blake3::hash(&data).into(); + let h: [u8; 32] = blake3::hash(&data[..size as usize]).into(); u64::from_be_bytes([h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]]) }; env.process @@ -210,6 +211,9 @@ impl JournalEffector { .unwrap() .snapshot_memory_hash .insert(region.into(), hash); + + // Shift the data pointer + data = &data[size as usize..]; } Ok(()) diff --git a/lib/wasix/src/syscalls/journal.rs b/lib/wasix/src/syscalls/journal.rs index 9131a7fa680..2b71591880a 100644 --- a/lib/wasix/src/syscalls/journal.rs +++ b/lib/wasix/src/syscalls/journal.rs @@ -155,22 +155,23 @@ pub unsafe fn restore_snapshot( journal: Arc, bootstrapping: bool, ) -> Result, WasiRuntimeError> { - use std::ops::Range; + use std::{collections::BTreeMap, ops::Range}; - use crate::journal::Journal; + use crate::{journal::Journal, os::task::process::MemorySnapshotRegion}; // We delay the spawning of threads until the end as its // possible that the threads will be cancelled before all the // events finished the streaming process - let mut spawn_threads: HashMap = Default::default(); - let mut staging_spawn_threads: HashMap = Default::default(); + let mut spawn_threads: BTreeMap = Default::default(); + let mut staging_spawn_threads: BTreeMap = Default::default(); let mut staging_kill_thread: HashSet = Default::default(); // We delay the memory updates until the end as its possible the // memory will be cleared before all the events finished the // streaming process - let mut update_memory: HashMap, Cow<'_, [u8]>> = Default::default(); - let mut staging_update_memory: HashMap, Cow<'_, [u8]>> = Default::default(); + let mut update_memory: BTreeMap> = Default::default(); + let mut staging_update_memory: BTreeMap> = + Default::default(); let mut update_tty = None; // We capture the stdout and stderr while we replay @@ -244,7 +245,7 @@ pub unsafe fn restore_snapshot( } if bootstrapping { - staging_update_memory.insert(region, data.clone()); + staging_update_memory.insert(region.into(), data.clone()); } else { JournalEffector::apply_memory(&mut ctx, region, &data) .map_err(anyhow_err_to_runtime_err)?; @@ -369,10 +370,10 @@ pub unsafe fn restore_snapshot( for thread_id in staging_kill_thread.drain() { spawn_threads.remove(&thread_id); } - for thread in staging_spawn_threads.drain() { + while let Some(thread) = staging_spawn_threads.pop_first() { spawn_threads.insert(thread.0, thread.1); } - for mem in staging_update_memory.drain() { + while let Some(mem) = staging_update_memory.pop_first() { update_memory.insert(mem.0, mem.1); } @@ -759,12 +760,14 @@ pub unsafe fn restore_snapshot( } .map_err(anyhow_err_to_runtime_err)?; } + // Next we apply all the memory updates that were delayed while the logs // were processed to completion. for (region, data) in update_memory { - JournalEffector::apply_memory(&mut ctx, region, &data) + JournalEffector::apply_memory(&mut ctx, region.into(), &data) .map_err(anyhow_err_to_runtime_err)?; } + if let Some(state) = update_tty { JournalEffector::apply_tty_set(&mut ctx, state).map_err(anyhow_err_to_runtime_err)?; } From d93a2fd954f8aa10b804fb5736c053e5faf4d56e Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 8 Feb 2024 20:40:48 +1100 Subject: [PATCH 35/67] Added extra data for the threads so they can be more easily recreated --- lib/journal/src/concrete/archived.rs | 38 + lib/journal/src/concrete/archived_from.rs | 73 + lib/journal/src/entry.rs | 7 + lib/wasi-types/src/wasix/mod.rs | 24 + lib/wasix/src/journal/concrete/archived.rs | 3529 ----------------- .../src/journal/effector/thread_state.rs | 33 + lib/wasix/src/os/task/process.rs | 16 +- lib/wasix/src/os/task/thread.rs | 23 +- lib/wasix/src/rewind.rs | 9 +- lib/wasix/src/state/env.rs | 6 +- lib/wasix/src/syscalls/journal.rs | 42 +- lib/wasix/src/syscalls/mod.rs | 8 + lib/wasix/src/syscalls/wasix/thread_spawn.rs | 5 +- 13 files changed, 259 insertions(+), 3554 deletions(-) delete mode 100644 lib/wasix/src/journal/concrete/archived.rs diff --git a/lib/journal/src/concrete/archived.rs b/lib/journal/src/concrete/archived.rs index f77b0acb443..32e7e260f41 100644 --- a/lib/journal/src/concrete/archived.rs +++ b/lib/journal/src/concrete/archived.rs @@ -451,11 +451,15 @@ impl<'a> JournalEntry<'a> { memory_stack, store_data, is_64bit, + start, + layout, } => serializer.serialize_value(&JournalEntrySetThreadV1 { id, call_stack: call_stack.into(), memory_stack: memory_stack.into(), store_data: store_data.into(), + start: start.into(), + layout: layout.into(), is_64bit, }), JournalEntry::CloseThreadV1 { id, exit_code } => { @@ -955,6 +959,8 @@ pub struct JournalEntrySetThreadV1<'a> { pub call_stack: AlignedCowVec<'a, u8>, pub memory_stack: AlignedCowVec<'a, u8>, pub store_data: AlignedCowVec<'a, u8>, + pub start: JournalThreadStartTypeV1, + pub layout: JournalWasiMemoryLayout, pub is_64bit: bool, } @@ -1747,3 +1753,35 @@ pub enum JournalSocketShutdownV1 { Write, Both, } + +#[repr(C)] +#[repr(align(8))] +#[derive( + Debug, + Clone, + Copy, + RkyvSerialize, + RkyvDeserialize, + Archive, + PartialOrd, + Ord, + PartialEq, + Eq, + Hash, +)] +#[archive_attr(derive(CheckBytes), repr(align(8)))] +pub enum JournalThreadStartTypeV1 { + MainThread, + ThreadSpawn { start_ptr: u64 }, +} + +#[repr(C)] +#[repr(align(8))] +#[derive(Debug, Clone, Copy, RkyvSerialize, RkyvDeserialize, Archive, PartialEq, Eq, Hash)] +#[archive_attr(derive(CheckBytes), repr(align(8)))] +pub struct JournalWasiMemoryLayout { + pub stack_upper: u64, + pub stack_lower: u64, + pub guard_size: u64, + pub stack_size: u64, +} diff --git a/lib/journal/src/concrete/archived_from.rs b/lib/journal/src/concrete/archived_from.rs index 93e59f66cce..4cbb645666f 100644 --- a/lib/journal/src/concrete/archived_from.rs +++ b/lib/journal/src/concrete/archived_from.rs @@ -2,6 +2,7 @@ use lz4_flex::block::decompress_size_prepended; use std::borrow::Cow; use std::time::SystemTime; use wasmer_wasix_types::wasi; +use wasmer_wasix_types::wasix::{ThreadStartType, WasiMemoryLayout}; use super::*; @@ -569,6 +570,74 @@ impl From<&'_ ArchivedJournalSocketShutdownV1> for SocketShutdownHow { } } +impl From for ThreadStartType { + fn from(value: JournalThreadStartTypeV1) -> Self { + match value { + JournalThreadStartTypeV1::MainThread => ThreadStartType::MainThread, + JournalThreadStartTypeV1::ThreadSpawn { start_ptr } => { + ThreadStartType::ThreadSpawn { start_ptr } + } + } + } +} + +impl From<&'_ ArchivedJournalThreadStartTypeV1> for ThreadStartType { + fn from(value: &'_ ArchivedJournalThreadStartTypeV1) -> Self { + match value { + ArchivedJournalThreadStartTypeV1::MainThread => ThreadStartType::MainThread, + ArchivedJournalThreadStartTypeV1::ThreadSpawn { start_ptr } => { + ThreadStartType::ThreadSpawn { + start_ptr: *start_ptr, + } + } + } + } +} + +impl From for JournalThreadStartTypeV1 { + fn from(value: ThreadStartType) -> Self { + match value { + ThreadStartType::MainThread => JournalThreadStartTypeV1::MainThread, + ThreadStartType::ThreadSpawn { start_ptr } => { + JournalThreadStartTypeV1::ThreadSpawn { start_ptr } + } + } + } +} + +impl From for WasiMemoryLayout { + fn from(value: JournalWasiMemoryLayout) -> Self { + Self { + stack_upper: value.stack_upper, + stack_lower: value.stack_lower, + guard_size: value.guard_size, + stack_size: value.stack_size, + } + } +} + +impl From<&'_ ArchivedJournalWasiMemoryLayout> for WasiMemoryLayout { + fn from(value: &'_ ArchivedJournalWasiMemoryLayout) -> Self { + Self { + stack_upper: value.stack_upper, + stack_lower: value.stack_lower, + guard_size: value.guard_size, + stack_size: value.stack_size, + } + } +} + +impl From for JournalWasiMemoryLayout { + fn from(value: WasiMemoryLayout) -> Self { + Self { + stack_upper: value.stack_upper, + stack_lower: value.stack_lower, + guard_size: value.guard_size, + stack_size: value.stack_size, + } + } +} + impl<'a> TryFrom> for JournalEntry<'a> { type Error = anyhow::Error; @@ -600,11 +669,15 @@ impl<'a> TryFrom> for JournalEntry<'a> { memory_stack, store_data, is_64bit, + start, + layout, }) => Self::SetThreadV1 { id: *id, call_stack: call_stack.as_ref().into(), memory_stack: memory_stack.as_ref().into(), store_data: store_data.as_ref().into(), + start: start.into(), + layout: layout.into(), is_64bit: *is_64bit, }, ArchivedJournalEntry::CloseThreadV1(ArchivedJournalEntryCloseThreadV1 { diff --git a/lib/journal/src/entry.rs b/lib/journal/src/entry.rs index 2f8862f0200..24b79b344d9 100644 --- a/lib/journal/src/entry.rs +++ b/lib/journal/src/entry.rs @@ -10,6 +10,7 @@ use wasmer_wasix_types::wasi::{ Filesize, Fstflags, LookupFlags, Oflags, Rights, SiFlags, Snapshot0Clockid, SockProto, Sockoption, Socktype, Timestamp, Tty, Whence, }; +use wasmer_wasix_types::wasix::{ThreadStartType, WasiMemoryLayout}; use crate::{base64, SnapshotTrigger}; @@ -105,6 +106,8 @@ pub enum JournalEntry<'a> { #[derivative(Debug = "ignore")] #[serde(with = "base64")] store_data: Cow<'a, [u8]>, + start: ThreadStartType, + layout: WasiMemoryLayout, is_64bit: bool, }, CloseThreadV1 { @@ -382,11 +385,15 @@ impl<'a> JournalEntry<'a> { memory_stack, store_data, is_64bit, + start, + layout, } => JournalEntry::SetThreadV1 { id, call_stack: call_stack.into_owned().into(), memory_stack: memory_stack.into_owned().into(), store_data: store_data.into_owned().into(), + start, + layout, is_64bit, }, Self::CloseThreadV1 { id, exit_code } => JournalEntry::CloseThreadV1 { id, exit_code }, diff --git a/lib/wasi-types/src/wasix/mod.rs b/lib/wasi-types/src/wasix/mod.rs index 4f664f54f58..c9a319a607b 100644 --- a/lib/wasi-types/src/wasix/mod.rs +++ b/lib/wasi-types/src/wasix/mod.rs @@ -1 +1,25 @@ +use serde::*; + // pub mod wasix_http_client_v1; + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialOrd, Ord, PartialEq, Eq, Hash)] +#[serde(rename_all = "snake_case")] +pub enum ThreadStartType { + MainThread, + ThreadSpawn { start_ptr: u64 }, +} + +/// Represents the memory layout of the parts that the thread itself uses +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct WasiMemoryLayout { + /// This is the top part of the stack (stacks go backwards) + pub stack_upper: u64, + /// This is the bottom part of the stack (anything more below this is a stack overflow) + pub stack_lower: u64, + /// Piece of memory that is marked as none readable/writable so stack overflows cause an exception + /// TODO: This field will need to be used to mark the guard memory as inaccessible + #[allow(dead_code)] + pub guard_size: u64, + /// Total size of the stack + pub stack_size: u64, +} diff --git a/lib/wasix/src/journal/concrete/archived.rs b/lib/wasix/src/journal/concrete/archived.rs deleted file mode 100644 index e7a822a6742..00000000000 --- a/lib/wasix/src/journal/concrete/archived.rs +++ /dev/null @@ -1,3529 +0,0 @@ -use lz4_flex::block::{compress_prepend_size, decompress_size_prepended}; -use num_enum::{IntoPrimitive, TryFromPrimitive}; -use rkyv::ser::{ScratchSpace, Serializer}; -use rkyv::{Archive, CheckBytes, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; -use std::borrow::Cow; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; -use std::time::{Duration, SystemTime}; -use virtual_net::{IpCidr, StreamSecurity}; -use wasmer_wasix_types::wasi::{self, EpollEventCtl, EpollType, Fdflags, Rights, Sockoption}; - -use super::*; - -pub const JOURNAL_MAGIC_NUMBER: u64 = 0x310d6dd027362979; -pub const JOURNAL_MAGIC_NUMBER_BYTES: [u8; 8] = JOURNAL_MAGIC_NUMBER.to_be_bytes(); - -#[repr(u16)] -#[derive( - Debug, - Clone, - Copy, - PartialEq, - Eq, - IntoPrimitive, - TryFromPrimitive, - RkyvSerialize, - RkyvDeserialize, - Archive, -)] -#[archive_attr(derive(CheckBytes))] -pub enum JournalEntryRecordType { - InitModuleV1 = 1, - ProcessExitV1 = 2, - SetThreadV1 = 3, - CloseThreadV1 = 4, - FileDescriptorSeekV1 = 5, - FileDescriptorWriteV1 = 6, - UpdateMemoryRegionV1 = 7, - SetClockTimeV1 = 9, - OpenFileDescriptorV1 = 10, - CloseFileDescriptorV1 = 11, - RenumberFileDescriptorV1 = 12, - DuplicateFileDescriptorV1 = 13, - CreateDirectoryV1 = 14, - RemoveDirectoryV1 = 15, - PathSetTimesV1 = 16, - FileDescriptorSetTimesV1 = 17, - FileDescriptorSetSizeV1 = 18, - FileDescriptorSetFlagsV1 = 19, - FileDescriptorSetRightsV1 = 20, - FileDescriptorAdviseV1 = 21, - FileDescriptorAllocateV1 = 22, - CreateHardLinkV1 = 23, - CreateSymbolicLinkV1 = 24, - UnlinkFileV1 = 25, - PathRenameV1 = 26, - ChangeDirectoryV1 = 27, - EpollCreateV1 = 28, - EpollCtlV1 = 29, - TtySetV1 = 30, - CreatePipeV1 = 31, - CreateEventV1 = 32, - PortAddAddrV1 = 33, - PortDelAddrV1 = 34, - PortAddrClearV1 = 35, - PortBridgeV1 = 36, - PortUnbridgeV1 = 37, - PortDhcpAcquireV1 = 38, - PortGatewaySetV1 = 39, - PortRouteAddV1 = 40, - PortRouteClearV1 = 41, - PortRouteDelV1 = 42, - SocketOpenV1 = 43, - SocketListenV1 = 44, - SocketBindV1 = 45, - SocketConnectedV1 = 46, - SocketAcceptedV1 = 47, - SocketJoinIpv4MulticastV1 = 48, - SocketJoinIpv6MulticastV1 = 49, - SocketLeaveIpv4MulticastV1 = 50, - SocketLeaveIpv6MulticastV1 = 51, - SocketSendFileV1 = 52, - SocketSendToV1 = 53, - SocketSendV1 = 54, - SocketSetOptFlagV1 = 55, - SocketSetOptSizeV1 = 56, - SocketSetOptTimeV1 = 57, - SocketShutdownV1 = 58, - SnapshotV1 = 59, -} - -impl JournalEntryRecordType { - /// # Safety - /// - /// `rykv` makes direct memory references to achieve high performance - /// however this does mean care must be taken that the data itself - /// can not be manipulated or corrupted. - pub unsafe fn deserialize_archive(self, data: &[u8]) -> anyhow::Result> { - match self { - JournalEntryRecordType::InitModuleV1 => ArchivedJournalEntry::InitModuleV1( - rkyv::archived_root::(data), - ), - JournalEntryRecordType::ProcessExitV1 => ArchivedJournalEntry::ProcessExitV1( - rkyv::archived_root::(data), - ), - JournalEntryRecordType::SetThreadV1 => ArchivedJournalEntry::SetThreadV1( - rkyv::archived_root::(data), - ), - JournalEntryRecordType::CloseThreadV1 => ArchivedJournalEntry::CloseThreadV1( - rkyv::archived_root::(data), - ), - JournalEntryRecordType::FileDescriptorSeekV1 => { - ArchivedJournalEntry::FileDescriptorSeekV1(rkyv::archived_root::< - JournalEntryFileDescriptorSeekV1, - >(data)) - } - JournalEntryRecordType::FileDescriptorWriteV1 => { - ArchivedJournalEntry::FileDescriptorWriteV1(rkyv::archived_root::< - JournalEntryFileDescriptorWriteV1, - >(data)) - } - JournalEntryRecordType::UpdateMemoryRegionV1 => { - ArchivedJournalEntry::UpdateMemoryRegionV1(rkyv::archived_root::< - JournalEntryUpdateMemoryRegionV1, - >(data)) - } - JournalEntryRecordType::SetClockTimeV1 => { - ArchivedJournalEntry::SetClockTimeV1(rkyv::archived_root::< - JournalEntrySetClockTimeV1, - >(data)) - } - JournalEntryRecordType::OpenFileDescriptorV1 => { - ArchivedJournalEntry::OpenFileDescriptorV1(rkyv::archived_root::< - JournalEntryOpenFileDescriptorV1, - >(data)) - } - JournalEntryRecordType::CloseFileDescriptorV1 => { - ArchivedJournalEntry::CloseFileDescriptorV1(rkyv::archived_root::< - JournalEntryCloseFileDescriptorV1, - >(data)) - } - JournalEntryRecordType::RenumberFileDescriptorV1 => { - ArchivedJournalEntry::RenumberFileDescriptorV1(rkyv::archived_root::< - JournalEntryRenumberFileDescriptorV1, - >(data)) - } - JournalEntryRecordType::DuplicateFileDescriptorV1 => { - ArchivedJournalEntry::DuplicateFileDescriptorV1(rkyv::archived_root::< - JournalEntryDuplicateFileDescriptorV1, - >(data)) - } - JournalEntryRecordType::CreateDirectoryV1 => { - ArchivedJournalEntry::CreateDirectoryV1(rkyv::archived_root::< - JournalEntryCreateDirectoryV1, - >(data)) - } - JournalEntryRecordType::RemoveDirectoryV1 => { - ArchivedJournalEntry::RemoveDirectoryV1(rkyv::archived_root::< - JournalEntryRemoveDirectoryV1, - >(data)) - } - JournalEntryRecordType::PathSetTimesV1 => { - ArchivedJournalEntry::PathSetTimesV1(rkyv::archived_root::< - JournalEntryPathSetTimesV1, - >(data)) - } - JournalEntryRecordType::FileDescriptorSetTimesV1 => { - ArchivedJournalEntry::FileDescriptorSetTimesV1(rkyv::archived_root::< - JournalEntryFileDescriptorSetTimesV1, - >(data)) - } - JournalEntryRecordType::FileDescriptorSetSizeV1 => { - ArchivedJournalEntry::FileDescriptorSetSizeV1(rkyv::archived_root::< - JournalEntryFileDescriptorSetSizeV1, - >(data)) - } - JournalEntryRecordType::FileDescriptorSetFlagsV1 => { - ArchivedJournalEntry::FileDescriptorSetFlagsV1(rkyv::archived_root::< - JournalEntryFileDescriptorSetFlagsV1, - >(data)) - } - JournalEntryRecordType::FileDescriptorSetRightsV1 => { - ArchivedJournalEntry::FileDescriptorSetRightsV1(rkyv::archived_root::< - JournalEntryFileDescriptorSetRightsV1, - >(data)) - } - JournalEntryRecordType::FileDescriptorAdviseV1 => { - ArchivedJournalEntry::FileDescriptorAdviseV1(rkyv::archived_root::< - JournalEntryFileDescriptorAdviseV1, - >(data)) - } - JournalEntryRecordType::FileDescriptorAllocateV1 => { - ArchivedJournalEntry::FileDescriptorAllocateV1(rkyv::archived_root::< - JournalEntryFileDescriptorAllocateV1, - >(data)) - } - JournalEntryRecordType::CreateHardLinkV1 => { - ArchivedJournalEntry::CreateHardLinkV1(rkyv::archived_root::< - JournalEntryCreateHardLinkV1, - >(data)) - } - JournalEntryRecordType::CreateSymbolicLinkV1 => { - ArchivedJournalEntry::CreateSymbolicLinkV1(rkyv::archived_root::< - JournalEntryCreateSymbolicLinkV1, - >(data)) - } - JournalEntryRecordType::UnlinkFileV1 => ArchivedJournalEntry::UnlinkFileV1( - rkyv::archived_root::(data), - ), - JournalEntryRecordType::PathRenameV1 => ArchivedJournalEntry::PathRenameV1( - rkyv::archived_root::(data), - ), - JournalEntryRecordType::ChangeDirectoryV1 => { - ArchivedJournalEntry::ChangeDirectoryV1(rkyv::archived_root::< - JournalEntryChangeDirectoryV1, - >(data)) - } - JournalEntryRecordType::EpollCreateV1 => ArchivedJournalEntry::EpollCreateV1( - rkyv::archived_root::(data), - ), - JournalEntryRecordType::EpollCtlV1 => ArchivedJournalEntry::EpollCtlV1( - rkyv::archived_root::(data), - ), - JournalEntryRecordType::TtySetV1 => { - ArchivedJournalEntry::TtySetV1(rkyv::archived_root::(data)) - } - JournalEntryRecordType::CreatePipeV1 => ArchivedJournalEntry::CreatePipeV1( - rkyv::archived_root::(data), - ), - JournalEntryRecordType::CreateEventV1 => ArchivedJournalEntry::CreateEventV1( - rkyv::archived_root::(data), - ), - JournalEntryRecordType::PortAddAddrV1 => ArchivedJournalEntry::PortAddAddrV1( - rkyv::archived_root::(data), - ), - JournalEntryRecordType::PortDelAddrV1 => ArchivedJournalEntry::PortDelAddrV1( - rkyv::archived_root::(data), - ), - JournalEntryRecordType::PortAddrClearV1 => return Ok(JournalEntry::PortAddrClearV1), - JournalEntryRecordType::PortBridgeV1 => ArchivedJournalEntry::PortBridgeV1( - rkyv::archived_root::(data), - ), - JournalEntryRecordType::PortUnbridgeV1 => return Ok(JournalEntry::PortUnbridgeV1), - JournalEntryRecordType::PortDhcpAcquireV1 => { - return Ok(JournalEntry::PortDhcpAcquireV1) - } - JournalEntryRecordType::PortGatewaySetV1 => { - ArchivedJournalEntry::PortGatewaySetV1(rkyv::archived_root::< - JournalEntryPortGatewaySetV1, - >(data)) - } - JournalEntryRecordType::PortRouteAddV1 => { - ArchivedJournalEntry::PortRouteAddV1(rkyv::archived_root::< - JournalEntryPortRouteAddV1, - >(data)) - } - JournalEntryRecordType::PortRouteClearV1 => return Ok(JournalEntry::PortRouteClearV1), - JournalEntryRecordType::PortRouteDelV1 => { - ArchivedJournalEntry::PortRouteDelV1(rkyv::archived_root::< - JournalEntryPortRouteDelV1, - >(data)) - } - JournalEntryRecordType::SocketOpenV1 => ArchivedJournalEntry::SocketOpenV1( - rkyv::archived_root::(data), - ), - JournalEntryRecordType::SocketListenV1 => { - ArchivedJournalEntry::SocketListenV1(rkyv::archived_root::< - JournalEntrySocketListenV1, - >(data)) - } - JournalEntryRecordType::SocketBindV1 => ArchivedJournalEntry::SocketBindV1( - rkyv::archived_root::(data), - ), - JournalEntryRecordType::SocketConnectedV1 => { - ArchivedJournalEntry::SocketConnectedV1(rkyv::archived_root::< - JournalEntrySocketConnectedV1, - >(data)) - } - JournalEntryRecordType::SocketAcceptedV1 => { - ArchivedJournalEntry::SocketAcceptedV1(rkyv::archived_root::< - JournalEntrySocketAcceptedV1, - >(data)) - } - JournalEntryRecordType::SocketJoinIpv4MulticastV1 => { - ArchivedJournalEntry::SocketJoinIpv4MulticastV1(rkyv::archived_root::< - JournalEntrySocketJoinIpv4MulticastV1, - >(data)) - } - JournalEntryRecordType::SocketJoinIpv6MulticastV1 => { - ArchivedJournalEntry::SocketJoinIpv6MulticastV1(rkyv::archived_root::< - JournalEntrySocketJoinIpv6MulticastV1, - >(data)) - } - JournalEntryRecordType::SocketLeaveIpv4MulticastV1 => { - ArchivedJournalEntry::SocketLeaveIpv4MulticastV1(rkyv::archived_root::< - JournalEntrySocketLeaveIpv4MulticastV1, - >(data)) - } - JournalEntryRecordType::SocketLeaveIpv6MulticastV1 => { - ArchivedJournalEntry::SocketLeaveIpv6MulticastV1(rkyv::archived_root::< - JournalEntrySocketLeaveIpv6MulticastV1, - >(data)) - } - JournalEntryRecordType::SocketSendFileV1 => { - ArchivedJournalEntry::SocketSendFileV1(rkyv::archived_root::< - JournalEntrySocketSendFileV1, - >(data)) - } - JournalEntryRecordType::SocketSendToV1 => { - ArchivedJournalEntry::SocketSendToV1(rkyv::archived_root::< - JournalEntrySocketSendToV1, - >(data)) - } - JournalEntryRecordType::SocketSendV1 => ArchivedJournalEntry::SocketSendV1( - rkyv::archived_root::(data), - ), - JournalEntryRecordType::SocketSetOptFlagV1 => { - ArchivedJournalEntry::SocketSetOptFlagV1(rkyv::archived_root::< - JournalEntrySocketSetOptFlagV1, - >(data)) - } - JournalEntryRecordType::SocketSetOptSizeV1 => { - ArchivedJournalEntry::SocketSetOptSizeV1(rkyv::archived_root::< - JournalEntrySocketSetOptSizeV1, - >(data)) - } - JournalEntryRecordType::SocketSetOptTimeV1 => { - ArchivedJournalEntry::SocketSetOptTimeV1(rkyv::archived_root::< - JournalEntrySocketSetOptTimeV1, - >(data)) - } - JournalEntryRecordType::SocketShutdownV1 => { - ArchivedJournalEntry::SocketShutdownV1(rkyv::archived_root::< - JournalEntrySocketShutdownV1, - >(data)) - } - JournalEntryRecordType::SnapshotV1 => ArchivedJournalEntry::SnapshotV1( - rkyv::archived_root::(data), - ), - } - .try_into() - } -} - -impl<'a> JournalEntry<'a> { - pub fn archive_record_type(&self) -> JournalEntryRecordType { - match self { - Self::InitModuleV1 { .. } => JournalEntryRecordType::InitModuleV1, - Self::UpdateMemoryRegionV1 { .. } => JournalEntryRecordType::UpdateMemoryRegionV1, - Self::ProcessExitV1 { .. } => JournalEntryRecordType::ProcessExitV1, - Self::SetThreadV1 { .. } => JournalEntryRecordType::SetThreadV1, - Self::CloseThreadV1 { .. } => JournalEntryRecordType::CloseThreadV1, - Self::FileDescriptorSeekV1 { .. } => JournalEntryRecordType::FileDescriptorSeekV1, - Self::FileDescriptorWriteV1 { .. } => JournalEntryRecordType::FileDescriptorWriteV1, - Self::SetClockTimeV1 { .. } => JournalEntryRecordType::SetClockTimeV1, - Self::CloseFileDescriptorV1 { .. } => JournalEntryRecordType::CloseFileDescriptorV1, - Self::OpenFileDescriptorV1 { .. } => JournalEntryRecordType::OpenFileDescriptorV1, - Self::RenumberFileDescriptorV1 { .. } => { - JournalEntryRecordType::RenumberFileDescriptorV1 - } - Self::DuplicateFileDescriptorV1 { .. } => { - JournalEntryRecordType::DuplicateFileDescriptorV1 - } - Self::CreateDirectoryV1 { .. } => JournalEntryRecordType::CreateDirectoryV1, - Self::RemoveDirectoryV1 { .. } => JournalEntryRecordType::RemoveDirectoryV1, - Self::PathSetTimesV1 { .. } => JournalEntryRecordType::PathSetTimesV1, - Self::FileDescriptorSetTimesV1 { .. } => { - JournalEntryRecordType::FileDescriptorSetTimesV1 - } - Self::FileDescriptorSetFlagsV1 { .. } => { - JournalEntryRecordType::FileDescriptorSetFlagsV1 - } - Self::FileDescriptorSetRightsV1 { .. } => { - JournalEntryRecordType::FileDescriptorSetRightsV1 - } - Self::FileDescriptorSetSizeV1 { .. } => JournalEntryRecordType::FileDescriptorSetSizeV1, - Self::FileDescriptorAdviseV1 { .. } => JournalEntryRecordType::FileDescriptorAdviseV1, - Self::FileDescriptorAllocateV1 { .. } => { - JournalEntryRecordType::FileDescriptorAllocateV1 - } - Self::CreateHardLinkV1 { .. } => JournalEntryRecordType::CreateHardLinkV1, - Self::CreateSymbolicLinkV1 { .. } => JournalEntryRecordType::CreateSymbolicLinkV1, - Self::UnlinkFileV1 { .. } => JournalEntryRecordType::UnlinkFileV1, - Self::PathRenameV1 { .. } => JournalEntryRecordType::PathRenameV1, - Self::ChangeDirectoryV1 { .. } => JournalEntryRecordType::ChangeDirectoryV1, - Self::EpollCreateV1 { .. } => JournalEntryRecordType::EpollCreateV1, - Self::EpollCtlV1 { .. } => JournalEntryRecordType::EpollCtlV1, - Self::TtySetV1 { .. } => JournalEntryRecordType::TtySetV1, - Self::CreatePipeV1 { .. } => JournalEntryRecordType::CreatePipeV1, - Self::CreateEventV1 { .. } => JournalEntryRecordType::CreateEventV1, - Self::PortAddAddrV1 { .. } => JournalEntryRecordType::PortAddAddrV1, - Self::PortDelAddrV1 { .. } => JournalEntryRecordType::PortDelAddrV1, - Self::PortAddrClearV1 => JournalEntryRecordType::PortAddrClearV1, - Self::PortBridgeV1 { .. } => JournalEntryRecordType::PortBridgeV1, - Self::PortUnbridgeV1 => JournalEntryRecordType::PortUnbridgeV1, - Self::PortDhcpAcquireV1 => JournalEntryRecordType::PortDhcpAcquireV1, - Self::PortGatewaySetV1 { .. } => JournalEntryRecordType::PortGatewaySetV1, - Self::PortRouteAddV1 { .. } => JournalEntryRecordType::PortRouteAddV1, - Self::PortRouteClearV1 => JournalEntryRecordType::PortRouteClearV1, - Self::PortRouteDelV1 { .. } => JournalEntryRecordType::PortRouteDelV1, - Self::SocketOpenV1 { .. } => JournalEntryRecordType::SocketOpenV1, - Self::SocketListenV1 { .. } => JournalEntryRecordType::SocketListenV1, - Self::SocketBindV1 { .. } => JournalEntryRecordType::SocketBindV1, - Self::SocketConnectedV1 { .. } => JournalEntryRecordType::SocketConnectedV1, - Self::SocketAcceptedV1 { .. } => JournalEntryRecordType::SocketAcceptedV1, - Self::SocketJoinIpv4MulticastV1 { .. } => { - JournalEntryRecordType::SocketJoinIpv4MulticastV1 - } - Self::SocketJoinIpv6MulticastV1 { .. } => { - JournalEntryRecordType::SocketJoinIpv6MulticastV1 - } - Self::SocketLeaveIpv4MulticastV1 { .. } => { - JournalEntryRecordType::SocketLeaveIpv4MulticastV1 - } - Self::SocketLeaveIpv6MulticastV1 { .. } => { - JournalEntryRecordType::SocketLeaveIpv6MulticastV1 - } - Self::SocketSendFileV1 { .. } => JournalEntryRecordType::SocketSendFileV1, - Self::SocketSendToV1 { .. } => JournalEntryRecordType::SocketSendToV1, - Self::SocketSendV1 { .. } => JournalEntryRecordType::SocketSendV1, - Self::SocketSetOptFlagV1 { .. } => JournalEntryRecordType::SocketSetOptFlagV1, - Self::SocketSetOptSizeV1 { .. } => JournalEntryRecordType::SocketSetOptSizeV1, - Self::SocketSetOptTimeV1 { .. } => JournalEntryRecordType::SocketSetOptTimeV1, - Self::SocketShutdownV1 { .. } => JournalEntryRecordType::SocketShutdownV1, - Self::SnapshotV1 { .. } => JournalEntryRecordType::SnapshotV1, - } - } - - pub fn serialize_archive( - self, - serializer: &mut T, - ) -> anyhow::Result<()> - where - T::Error: std::fmt::Display, - { - let padding = |size: usize| { - let padding = size % 16; - let padding = match padding { - 0 => 0, - a => 16 - a, - }; - vec![0u8; padding] - }; - match self { - JournalEntry::InitModuleV1 { wasm_hash } => { - serializer.serialize_value(&JournalEntryInitModuleV1 { wasm_hash }) - } - JournalEntry::UpdateMemoryRegionV1 { region, data } => { - serializer.serialize_value(&JournalEntryUpdateMemoryRegionV1 { - start: region.start, - end: region.end, - compressed_data: to_aligned_vec(compress_prepend_size(data.as_ref())), - }) - } - JournalEntry::ProcessExitV1 { exit_code } => { - serializer.serialize_value(&JournalEntryProcessExitV1 { - exit_code: exit_code.map(|e| e.into()), - _padding: 0, - }) - } - JournalEntry::SetThreadV1 { - id, - call_stack, - memory_stack, - store_data, - is_64bit, - } => serializer.serialize_value(&JournalEntrySetThreadV1 { - id: id.into(), - call_stack: to_aligned_vec(call_stack), - memory_stack: to_aligned_vec_str(memory_stack), - store_data: to_aligned_vec_str(store_data), - is_64bit, - }), - JournalEntry::CloseThreadV1 { id, exit_code } => { - serializer.serialize_value(&JournalEntryCloseThreadV1 { - id: id.into(), - exit_code: exit_code.map(|e| e.into()), - }) - } - JournalEntry::FileDescriptorSeekV1 { fd, offset, whence } => serializer - .serialize_value(&JournalEntryFileDescriptorSeekV1 { - fd, - offset, - whence: whence.into(), - }), - JournalEntry::FileDescriptorWriteV1 { - fd, - offset, - data, - is_64bit, - } => serializer.serialize_value(&JournalEntryFileDescriptorWriteV1 { - fd, - offset, - data: to_aligned_vec(data), - is_64bit, - }), - JournalEntry::SetClockTimeV1 { clock_id, time } => { - serializer.serialize_value(&JournalEntrySetClockTimeV1 { - clock_id: clock_id.into(), - time, - }) - } - JournalEntry::CloseFileDescriptorV1 { fd } => { - serializer.serialize_value(&JournalEntryCloseFileDescriptorV1 { fd, _padding: 0 }) - } - JournalEntry::OpenFileDescriptorV1 { - fd, - dirfd, - dirflags, - path, - o_flags, - fs_rights_base, - fs_rights_inheriting, - fs_flags, - } => serializer.serialize_value(&JournalEntryOpenFileDescriptorV1 { - fd, - dirfd, - dirflags, - path: to_aligned_vec_str(path), - o_flags: o_flags.bits(), - fs_rights_base: fs_rights_base.bits(), - fs_rights_inheriting: fs_rights_inheriting.bits(), - fs_flags: fs_flags.bits(), - }), - JournalEntry::RenumberFileDescriptorV1 { old_fd, new_fd } => { - serializer.serialize_value(&JournalEntryRenumberFileDescriptorV1 { old_fd, new_fd }) - } - JournalEntry::DuplicateFileDescriptorV1 { - original_fd, - copied_fd, - } => serializer.serialize_value(&JournalEntryDuplicateFileDescriptorV1 { - original_fd, - copied_fd, - }), - JournalEntry::CreateDirectoryV1 { fd, path } => { - serializer.serialize_value(&JournalEntryCreateDirectoryV1 { - fd, - path: to_aligned_vec_str(path), - }) - } - JournalEntry::RemoveDirectoryV1 { fd, path } => { - serializer.serialize_value(&JournalEntryRemoveDirectoryV1 { - fd, - path: to_aligned_vec_str(path), - }) - } - JournalEntry::PathSetTimesV1 { - fd, - flags, - path, - st_atim, - st_mtim, - fst_flags, - } => serializer.serialize_value(&JournalEntryPathSetTimesV1 { - fd, - flags, - path: to_aligned_vec_str(path), - st_atim, - st_mtim, - fst_flags: fst_flags.bits(), - }), - JournalEntry::FileDescriptorSetTimesV1 { - fd, - st_atim, - st_mtim, - fst_flags, - } => serializer.serialize_value(&JournalEntryFileDescriptorSetTimesV1 { - fd, - st_atim, - st_mtim, - fst_flags: fst_flags.bits(), - }), - JournalEntry::FileDescriptorSetFlagsV1 { fd, flags } => { - serializer.serialize_value(&JournalEntryFileDescriptorSetFlagsV1 { - fd, - flags: flags.bits(), - }) - } - JournalEntry::FileDescriptorSetRightsV1 { - fd, - fs_rights_base, - fs_rights_inheriting, - } => serializer.serialize_value(&JournalEntryFileDescriptorSetRightsV1 { - fd, - fs_rights_base: fs_rights_base.bits(), - fs_rights_inheriting: fs_rights_inheriting.bits(), - }), - JournalEntry::FileDescriptorSetSizeV1 { fd, st_size } => { - serializer.serialize_value(&JournalEntryFileDescriptorSetSizeV1 { fd, st_size }) - } - JournalEntry::FileDescriptorAdviseV1 { - fd, - offset, - len, - advice, - } => serializer.serialize_value(&JournalEntryFileDescriptorAdviseV1 { - fd, - offset, - len, - advice: advice.into(), - }), - JournalEntry::FileDescriptorAllocateV1 { fd, offset, len } => serializer - .serialize_value(&JournalEntryFileDescriptorAllocateV1 { fd, offset, len }), - JournalEntry::CreateHardLinkV1 { - old_fd, - old_path, - old_flags, - new_fd, - new_path, - } => serializer.serialize_value(&JournalEntryCreateHardLinkV1 { - old_fd, - old_path: to_aligned_vec_str(old_path), - old_flags, - new_fd, - new_path: to_aligned_vec_str(new_path), - }), - JournalEntry::CreateSymbolicLinkV1 { - old_path, - fd, - new_path, - } => serializer.serialize_value(&JournalEntryCreateSymbolicLinkV1 { - old_path: to_aligned_vec_str(old_path), - fd, - new_path: to_aligned_vec_str(new_path), - }), - JournalEntry::UnlinkFileV1 { fd, path } => { - serializer.serialize_value(&JournalEntryUnlinkFileV1 { - fd, - path: to_aligned_vec_str(path), - }) - } - JournalEntry::PathRenameV1 { - old_fd, - old_path, - new_fd, - new_path, - } => serializer.serialize_value(&JournalEntryPathRenameV1 { - old_fd, - old_path: to_aligned_vec_str(old_path), - new_fd, - new_path: to_aligned_vec_str(new_path), - }), - JournalEntry::ChangeDirectoryV1 { path } => { - serializer.serialize_value(&JournalEntryChangeDirectoryV1 { - path: to_aligned_vec_str(path), - }) - } - JournalEntry::EpollCreateV1 { fd } => { - serializer.serialize_value(&JournalEntryEpollCreateV1 { fd, _padding: 0 }) - } - JournalEntry::EpollCtlV1 { - epfd, - op, - fd, - event, - } => serializer.serialize_value(&JournalEntryEpollCtlV1 { - epfd, - op: op.into(), - fd, - event: event.map(|e| e.into()), - }), - JournalEntry::TtySetV1 { tty, line_feeds } => { - serializer.serialize_value(&JournalEntryTtySetV1 { - cols: tty.cols, - rows: tty.rows, - width: tty.width, - height: tty.height, - stdin_tty: tty.stdin_tty, - stdout_tty: tty.stdout_tty, - stderr_tty: tty.stderr_tty, - echo: tty.echo, - line_buffered: tty.line_buffered, - line_feeds, - }) - } - JournalEntry::CreatePipeV1 { fd1, fd2 } => { - serializer.serialize_value(&JournalEntryCreatePipeV1 { fd1, fd2 }) - } - JournalEntry::CreateEventV1 { - initial_val, - flags, - fd, - } => serializer.serialize_value(&JournalEntryCreateEventV1 { - initial_val, - flags, - fd, - }), - JournalEntry::PortAddAddrV1 { cidr } => { - serializer.serialize_value(&JournalEntryPortAddAddrV1 { cidr }) - } - JournalEntry::PortDelAddrV1 { addr } => { - serializer.serialize_value(&JournalEntryPortDelAddrV1 { addr }) - } - JournalEntry::PortAddrClearV1 => return Ok(()), - JournalEntry::PortBridgeV1 { - network, - token, - security, - } => serializer.serialize_value(&JournalEntryPortBridgeV1 { - network: to_aligned_vec_str(network), - token: to_aligned_vec_str(token), - security: security.into(), - }), - JournalEntry::PortUnbridgeV1 => return Ok(()), - JournalEntry::PortDhcpAcquireV1 => return Ok(()), - JournalEntry::PortGatewaySetV1 { ip } => { - serializer.serialize_value(&JournalEntryPortGatewaySetV1 { ip }) - } - JournalEntry::PortRouteAddV1 { - cidr, - via_router, - preferred_until, - expires_at, - } => serializer.serialize_value(&JournalEntryPortRouteAddV1 { - cidr, - via_router, - preferred_until, - expires_at, - }), - JournalEntry::PortRouteClearV1 => return Ok(()), - JournalEntry::PortRouteDelV1 { ip } => { - serializer.serialize_value(&JournalEntryPortRouteDelV1 { ip }) - } - JournalEntry::SocketOpenV1 { af, ty, pt, fd } => { - serializer.serialize_value(&JournalEntrySocketOpenV1 { - af: af.into(), - ty: ty.into(), - pt: pt.into(), - fd, - }) - } - JournalEntry::SocketListenV1 { fd, backlog } => { - serializer.serialize_value(&JournalEntrySocketListenV1 { fd, backlog }) - } - JournalEntry::SocketBindV1 { fd, addr } => { - serializer.serialize_value(&JournalEntrySocketBindV1 { fd, addr }) - } - JournalEntry::SocketConnectedV1 { fd, addr } => { - serializer.serialize_value(&JournalEntrySocketConnectedV1 { fd, addr }) - } - JournalEntry::SocketAcceptedV1 { - listen_fd, - fd, - peer_addr, - fd_flags, - non_blocking: nonblocking, - } => serializer.serialize_value(&JournalEntrySocketAcceptedV1 { - listen_fd, - fd, - peer_addr, - fd_flags: fd_flags.bits(), - nonblocking, - }), - JournalEntry::SocketJoinIpv4MulticastV1 { - fd, - multiaddr, - iface, - } => serializer.serialize_value(&JournalEntrySocketJoinIpv4MulticastV1 { - fd, - multiaddr, - iface, - }), - JournalEntry::SocketJoinIpv6MulticastV1 { - fd, - multi_addr: multiaddr, - iface, - } => serializer.serialize_value(&JournalEntrySocketJoinIpv6MulticastV1 { - fd, - multiaddr, - iface, - }), - JournalEntry::SocketLeaveIpv4MulticastV1 { - fd, - multi_addr: multiaddr, - iface, - } => serializer.serialize_value(&JournalEntrySocketLeaveIpv4MulticastV1 { - fd, - multiaddr, - iface, - }), - JournalEntry::SocketLeaveIpv6MulticastV1 { - fd, - multi_addr: multiaddr, - iface, - } => serializer.serialize_value(&JournalEntrySocketLeaveIpv6MulticastV1 { - fd, - multiaddr, - iface, - }), - JournalEntry::SocketSendFileV1 { - socket_fd, - file_fd, - offset, - count, - } => serializer.serialize_value(&JournalEntrySocketSendFileV1 { - socket_fd, - file_fd, - offset, - count, - }), - JournalEntry::SocketSendToV1 { - fd, - data, - flags, - addr, - is_64bit, - } => serializer.serialize_value(&JournalEntrySocketSendToV1 { - fd, - _padding: padding(data.len()), - data: data.into_owned(), - flags, - addr, - is_64bit, - }), - JournalEntry::SocketSendV1 { - fd, - data, - flags, - is_64bit, - } => serializer.serialize_value(&JournalEntrySocketSendV1 { - fd, - data: to_aligned_vec(data), - flags, - is_64bit, - }), - JournalEntry::SocketSetOptFlagV1 { fd, opt, flag } => { - serializer.serialize_value(&JournalEntrySocketSetOptFlagV1 { - fd, - opt: opt.into(), - flag, - }) - } - JournalEntry::SocketSetOptSizeV1 { fd, opt, size } => { - serializer.serialize_value(&JournalEntrySocketSetOptSizeV1 { - fd, - opt: opt.into(), - size, - }) - } - JournalEntry::SocketSetOptTimeV1 { fd, ty, time } => { - serializer.serialize_value(&JournalEntrySocketSetOptTimeV1 { - fd, - ty: ty.into(), - time, - }) - } - JournalEntry::SocketShutdownV1 { fd, how } => { - serializer.serialize_value(&JournalEntrySocketShutdownV1 { - fd, - how: how.into(), - }) - } - JournalEntry::SnapshotV1 { when, trigger } => { - serializer.serialize_value(&JournalEntrySnapshotV1 { - since_epoch: when - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap_or(Duration::ZERO), - trigger: trigger.into(), - }) - } - } - .map_err(|err| anyhow::format_err!("failed to serialize journal record - {}", err))?; - Ok(()) - } -} - -/// The journal log entries are serializable which -/// allows them to be written directly to a file -/// -/// Note: This structure is versioned which allows for -/// changes to the journal entry types without having to -/// worry about backward and forward compatibility -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub(crate) struct JournalEntryHeader { - pub record_type: u16, - pub record_size: u64, -} - -pub enum ArchivedJournalEntry<'a> { - InitModuleV1(&'a ArchivedJournalEntryInitModuleV1), - ProcessExitV1(&'a ArchivedJournalEntryProcessExitV1), - SetThreadV1(&'a ArchivedJournalEntrySetThreadV1), - CloseThreadV1(&'a ArchivedJournalEntryCloseThreadV1), - FileDescriptorSeekV1(&'a ArchivedJournalEntryFileDescriptorSeekV1), - FileDescriptorWriteV1(&'a ArchivedJournalEntryFileDescriptorWriteV1<'a>), - UpdateMemoryRegionV1(&'a ArchivedJournalEntryUpdateMemoryRegionV1), - SetClockTimeV1(&'a ArchivedJournalEntrySetClockTimeV1), - OpenFileDescriptorV1(&'a ArchivedJournalEntryOpenFileDescriptorV1), - CloseFileDescriptorV1(&'a ArchivedJournalEntryCloseFileDescriptorV1), - RenumberFileDescriptorV1(&'a ArchivedJournalEntryRenumberFileDescriptorV1), - DuplicateFileDescriptorV1(&'a ArchivedJournalEntryDuplicateFileDescriptorV1), - CreateDirectoryV1(&'a ArchivedJournalEntryCreateDirectoryV1), - RemoveDirectoryV1(&'a ArchivedJournalEntryRemoveDirectoryV1), - PathSetTimesV1(&'a ArchivedJournalEntryPathSetTimesV1), - FileDescriptorSetTimesV1(&'a ArchivedJournalEntryFileDescriptorSetTimesV1), - FileDescriptorSetSizeV1(&'a ArchivedJournalEntryFileDescriptorSetSizeV1), - FileDescriptorSetFlagsV1(&'a ArchivedJournalEntryFileDescriptorSetFlagsV1), - FileDescriptorSetRightsV1(&'a ArchivedJournalEntryFileDescriptorSetRightsV1), - FileDescriptorAdviseV1(&'a ArchivedJournalEntryFileDescriptorAdviseV1), - FileDescriptorAllocateV1(&'a ArchivedJournalEntryFileDescriptorAllocateV1), - CreateHardLinkV1(&'a ArchivedJournalEntryCreateHardLinkV1), - CreateSymbolicLinkV1(&'a ArchivedJournalEntryCreateSymbolicLinkV1), - UnlinkFileV1(&'a ArchivedJournalEntryUnlinkFileV1), - PathRenameV1(&'a ArchivedJournalEntryPathRenameV1), - ChangeDirectoryV1(&'a ArchivedJournalEntryChangeDirectoryV1), - EpollCreateV1(&'a ArchivedJournalEntryEpollCreateV1), - EpollCtlV1(&'a ArchivedJournalEntryEpollCtlV1), - TtySetV1(&'a ArchivedJournalEntryTtySetV1), - CreatePipeV1(&'a ArchivedJournalEntryCreatePipeV1), - CreateEventV1(&'a ArchivedJournalEntryCreateEventV1), - PortAddAddrV1(&'a ArchivedJournalEntryPortAddAddrV1), - PortDelAddrV1(&'a ArchivedJournalEntryPortDelAddrV1), - PortAddrClearV1, - PortBridgeV1(&'a ArchivedJournalEntryPortBridgeV1), - PortUnbridgeV1, - PortDhcpAcquireV1, - PortGatewaySetV1(&'a ArchivedJournalEntryPortGatewaySetV1), - PortRouteAddV1(&'a ArchivedJournalEntryPortRouteAddV1), - PortRouteClearV1, - PortRouteDelV1(&'a ArchivedJournalEntryPortRouteDelV1), - SocketOpenV1(&'a ArchivedJournalEntrySocketOpenV1), - SocketListenV1(&'a ArchivedJournalEntrySocketListenV1), - SocketBindV1(&'a ArchivedJournalEntrySocketBindV1), - SocketConnectedV1(&'a ArchivedJournalEntrySocketConnectedV1), - SocketAcceptedV1(&'a ArchivedJournalEntrySocketAcceptedV1), - SocketJoinIpv4MulticastV1(&'a ArchivedJournalEntrySocketJoinIpv4MulticastV1), - SocketJoinIpv6MulticastV1(&'a ArchivedJournalEntrySocketJoinIpv6MulticastV1), - SocketLeaveIpv4MulticastV1(&'a ArchivedJournalEntrySocketLeaveIpv4MulticastV1), - SocketLeaveIpv6MulticastV1(&'a ArchivedJournalEntrySocketLeaveIpv6MulticastV1), - SocketSendFileV1(&'a ArchivedJournalEntrySocketSendFileV1), - SocketSendToV1(&'a ArchivedJournalEntrySocketSendToV1), - SocketSendV1(&'a ArchivedJournalEntrySocketSendV1), - SocketSetOptFlagV1(&'a ArchivedJournalEntrySocketSetOptFlagV1), - SocketSetOptSizeV1(&'a ArchivedJournalEntrySocketSetOptSizeV1), - SocketSetOptTimeV1(&'a ArchivedJournalEntrySocketSetOptTimeV1), - SocketShutdownV1(&'a ArchivedJournalEntrySocketShutdownV1), - SnapshotV1(&'a ArchivedJournalEntrySnapshotV1), -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryInitModuleV1 { - pub wasm_hash: [u8; 8], -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryProcessExitV1 { - pub exit_code: Option, - pub _padding: u32, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntrySetThreadV1 { - pub id: u32, - pub call_stack: Vec, - pub memory_stack: Vec, - pub store_data: Vec, - pub _padding: Vec, - pub is_64bit: bool, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryCloseThreadV1 { - pub id: u32, - pub exit_code: Option, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryFileDescriptorSeekV1 { - pub fd: u32, - pub offset: i64, - pub whence: JournalWhenceV1, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryFileDescriptorWriteV1 { - pub fd: u32, - pub offset: u64, - pub data: Vec, - pub _padding: Vec, - pub is_64bit: bool, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryUpdateMemoryRegionV1 { - pub start: u64, - pub end: u64, - pub compressed_data: Vec, - pub _padding: Vec, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntrySetClockTimeV1 { - pub clock_id: JournalSnapshot0ClockidV1, - pub time: u64, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryOpenFileDescriptorV1 { - pub fd: u32, - pub dirfd: u32, - pub dirflags: u32, - pub path: String, - pub _padding: Vec, - pub o_flags: u16, - pub fs_rights_base: u64, - pub fs_rights_inheriting: u64, - pub fs_flags: u16, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryCloseFileDescriptorV1 { - pub fd: u32, - pub _padding: u32, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryRenumberFileDescriptorV1 { - pub old_fd: u32, - pub new_fd: u32, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryDuplicateFileDescriptorV1 { - pub original_fd: u32, - pub copied_fd: u32, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryCreateDirectoryV1 { - pub fd: u32, - pub path: String, - pub _padding: Vec, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryRemoveDirectoryV1 { - pub fd: u32, - pub path: String, - pub _padding: Vec, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryPathSetTimesV1 { - pub fd: u32, - pub flags: u32, - pub path: String, - pub _padding: Vec, - pub st_atim: u64, - pub st_mtim: u64, - pub fst_flags: u16, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryFileDescriptorSetTimesV1 { - pub fd: u32, - pub st_atim: u64, - pub st_mtim: u64, - pub fst_flags: u16, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryFileDescriptorSetSizeV1 { - pub fd: u32, - pub st_size: u64, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryFileDescriptorSetFlagsV1 { - pub fd: u32, - pub flags: u16, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryFileDescriptorSetRightsV1 { - pub fd: u32, - pub fs_rights_base: u64, - pub fs_rights_inheriting: u64, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryFileDescriptorAdviseV1 { - pub fd: u32, - pub offset: u64, - pub len: u64, - pub advice: JournalAdviceV1, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryFileDescriptorAllocateV1 { - pub fd: u32, - pub offset: u64, - pub len: u64, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryCreateHardLinkV1 { - pub old_fd: u32, - pub old_path: String, - pub old_flags: u32, - pub new_fd: u32, - pub new_path: String, - pub _padding: Vec, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryCreateSymbolicLinkV1 { - pub old_path: String, - pub fd: u32, - pub new_path: String, - pub _padding: Vec, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryUnlinkFileV1 { - pub fd: u32, - pub path: String, - pub _padding: Vec, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryPathRenameV1 { - pub old_fd: u32, - pub old_path: String, - pub new_fd: u32, - pub new_path: String, - pub _padding: Vec, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryChangeDirectoryV1 { - pub path: String, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryEpollCreateV1 { - pub fd: u32, - pub _padding: u32, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryEpollCtlV1 { - pub epfd: u32, - pub op: JournalEpollCtlV1, - pub fd: u32, - pub event: Option, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryTtySetV1 { - pub cols: u32, - pub rows: u32, - pub width: u32, - pub height: u32, - pub stdin_tty: bool, - pub stdout_tty: bool, - pub stderr_tty: bool, - pub echo: bool, - pub line_buffered: bool, - pub line_feeds: bool, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryCreatePipeV1 { - pub fd1: u32, - pub fd2: u32, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryCreateEventV1 { - pub initial_val: u64, - pub flags: u16, - pub fd: u32, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryPortAddAddrV1 { - pub cidr: IpCidr, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryPortDelAddrV1 { - pub addr: IpAddr, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryPortBridgeV1 { - pub network: String, - pub token: String, - pub _padding: Vec, - pub security: JournalStreamSecurityV1, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryPortGatewaySetV1 { - pub ip: IpAddr, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryPortRouteAddV1 { - pub cidr: IpCidr, - pub via_router: IpAddr, - pub preferred_until: Option, - pub expires_at: Option, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntryPortRouteDelV1 { - pub ip: IpAddr, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntrySocketOpenV1 { - pub af: JournalAddressfamilyV1, - pub ty: JournalSocktypeV1, - pub pt: u16, - pub fd: u32, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntrySocketListenV1 { - pub fd: u32, - pub backlog: u32, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntrySocketBindV1 { - pub fd: u32, - pub addr: SocketAddr, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntrySocketConnectedV1 { - pub fd: u32, - pub addr: SocketAddr, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntrySocketAcceptedV1 { - pub listen_fd: u32, - pub fd: u32, - pub peer_addr: SocketAddr, - pub fd_flags: u16, - pub nonblocking: bool, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntrySocketJoinIpv4MulticastV1 { - pub fd: u32, - pub multiaddr: Ipv4Addr, - pub iface: Ipv4Addr, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntrySocketJoinIpv6MulticastV1 { - pub fd: u32, - pub multiaddr: Ipv6Addr, - pub iface: u32, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntrySocketLeaveIpv4MulticastV1 { - pub fd: u32, - pub multiaddr: Ipv4Addr, - pub iface: Ipv4Addr, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntrySocketLeaveIpv6MulticastV1 { - pub fd: u32, - pub multiaddr: Ipv6Addr, - pub iface: u32, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntrySocketSendFileV1 { - pub socket_fd: u32, - pub file_fd: u32, - pub offset: u64, - pub count: u64, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntrySocketSendToV1 { - pub fd: u32, - pub data: Vec, - pub _padding: Vec, - pub flags: u16, - pub addr: SocketAddr, - pub is_64bit: bool, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntrySocketSendV1 { - pub fd: u32, - pub data: Vec, - pub _padding: Vec, - pub flags: u16, - pub is_64bit: bool, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntrySocketSetOptFlagV1 { - pub fd: u32, - pub opt: JournalSockoptionV1, - pub flag: bool, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntrySocketSetOptSizeV1 { - pub fd: u32, - pub opt: JournalSockoptionV1, - pub size: u64, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntrySocketSetOptTimeV1 { - pub fd: u32, - pub ty: JournalTimeTypeV1, - pub time: Option, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntrySocketShutdownV1 { - pub fd: u32, - pub how: JournalSocketShutdownV1, -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEntrySnapshotV1 { - pub since_epoch: Duration, - pub trigger: JournalSnapshotTriggerV1, -} - -#[repr(C)] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub enum JournalSnapshot0ClockidV1 { - Realtime, - Monotonic, - ProcessCputimeId, - ThreadCputimeId, - Unknown = 255, -} - -impl From for JournalSnapshot0ClockidV1 { - fn from(val: wasi::Snapshot0Clockid) -> Self { - match val { - wasi::Snapshot0Clockid::Realtime => JournalSnapshot0ClockidV1::Realtime, - wasi::Snapshot0Clockid::Monotonic => JournalSnapshot0ClockidV1::Monotonic, - wasi::Snapshot0Clockid::ProcessCputimeId => JournalSnapshot0ClockidV1::ProcessCputimeId, - wasi::Snapshot0Clockid::ThreadCputimeId => JournalSnapshot0ClockidV1::ThreadCputimeId, - wasi::Snapshot0Clockid::Unknown => JournalSnapshot0ClockidV1::Unknown, - } - } -} - -impl From for wasi::Snapshot0Clockid { - fn from(val: JournalSnapshot0ClockidV1) -> Self { - match val { - JournalSnapshot0ClockidV1::Realtime => wasi::Snapshot0Clockid::Realtime, - JournalSnapshot0ClockidV1::Monotonic => wasi::Snapshot0Clockid::Monotonic, - JournalSnapshot0ClockidV1::ProcessCputimeId => wasi::Snapshot0Clockid::ProcessCputimeId, - JournalSnapshot0ClockidV1::ThreadCputimeId => wasi::Snapshot0Clockid::ThreadCputimeId, - JournalSnapshot0ClockidV1::Unknown => wasi::Snapshot0Clockid::Unknown, - } - } -} - -impl From<&'_ ArchivedJournalSnapshot0ClockidV1> for wasi::Snapshot0Clockid { - fn from(val: &'_ ArchivedJournalSnapshot0ClockidV1) -> Self { - match val { - ArchivedJournalSnapshot0ClockidV1::Realtime => wasi::Snapshot0Clockid::Realtime, - ArchivedJournalSnapshot0ClockidV1::Monotonic => wasi::Snapshot0Clockid::Monotonic, - ArchivedJournalSnapshot0ClockidV1::ProcessCputimeId => { - wasi::Snapshot0Clockid::ProcessCputimeId - } - ArchivedJournalSnapshot0ClockidV1::ThreadCputimeId => { - wasi::Snapshot0Clockid::ThreadCputimeId - } - ArchivedJournalSnapshot0ClockidV1::Unknown => wasi::Snapshot0Clockid::Unknown, - } - } -} - -#[repr(C)] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub enum JournalWhenceV1 { - Set, - Cur, - End, - Unknown = 255, -} - -impl From for JournalWhenceV1 { - fn from(val: wasi::Whence) -> Self { - match val { - wasi::Whence::Set => JournalWhenceV1::Set, - wasi::Whence::Cur => JournalWhenceV1::Cur, - wasi::Whence::End => JournalWhenceV1::End, - wasi::Whence::Unknown => JournalWhenceV1::Unknown, - } - } -} - -impl From for wasi::Whence { - fn from(val: JournalWhenceV1) -> Self { - match val { - JournalWhenceV1::Set => wasi::Whence::Set, - JournalWhenceV1::Cur => wasi::Whence::Cur, - JournalWhenceV1::End => wasi::Whence::End, - JournalWhenceV1::Unknown => wasi::Whence::Unknown, - } - } -} - -impl From<&'_ ArchivedJournalWhenceV1> for wasi::Whence { - fn from(val: &'_ ArchivedJournalWhenceV1) -> Self { - match val { - ArchivedJournalWhenceV1::Set => wasi::Whence::Set, - ArchivedJournalWhenceV1::Cur => wasi::Whence::Cur, - ArchivedJournalWhenceV1::End => wasi::Whence::End, - ArchivedJournalWhenceV1::Unknown => wasi::Whence::Unknown, - } - } -} - -#[repr(C)] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub enum JournalAdviceV1 { - Normal, - Sequential, - Random, - Willneed, - Dontneed, - Noreuse, - Unknown = 255, -} - -impl From for JournalAdviceV1 { - fn from(val: wasi::Advice) -> Self { - match val { - wasi::Advice::Normal => JournalAdviceV1::Normal, - wasi::Advice::Sequential => JournalAdviceV1::Sequential, - wasi::Advice::Random => JournalAdviceV1::Random, - wasi::Advice::Willneed => JournalAdviceV1::Willneed, - wasi::Advice::Dontneed => JournalAdviceV1::Dontneed, - wasi::Advice::Noreuse => JournalAdviceV1::Noreuse, - wasi::Advice::Unknown => JournalAdviceV1::Unknown, - } - } -} - -impl From for wasi::Advice { - fn from(val: JournalAdviceV1) -> Self { - match val { - JournalAdviceV1::Normal => wasi::Advice::Normal, - JournalAdviceV1::Sequential => wasi::Advice::Sequential, - JournalAdviceV1::Random => wasi::Advice::Random, - JournalAdviceV1::Willneed => wasi::Advice::Willneed, - JournalAdviceV1::Dontneed => wasi::Advice::Dontneed, - JournalAdviceV1::Noreuse => wasi::Advice::Noreuse, - JournalAdviceV1::Unknown => wasi::Advice::Unknown, - } - } -} - -impl From<&'_ ArchivedJournalAdviceV1> for wasi::Advice { - fn from(val: &'_ ArchivedJournalAdviceV1) -> Self { - match val { - ArchivedJournalAdviceV1::Normal => wasi::Advice::Normal, - ArchivedJournalAdviceV1::Sequential => wasi::Advice::Sequential, - ArchivedJournalAdviceV1::Random => wasi::Advice::Random, - ArchivedJournalAdviceV1::Willneed => wasi::Advice::Willneed, - ArchivedJournalAdviceV1::Dontneed => wasi::Advice::Dontneed, - ArchivedJournalAdviceV1::Noreuse => wasi::Advice::Noreuse, - ArchivedJournalAdviceV1::Unknown => wasi::Advice::Unknown, - } - } -} - -#[repr(C)] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub enum JournalExitCodeV1 { - Errno(u16), - Other(i32), -} - -impl From for JournalExitCodeV1 { - fn from(val: wasi::ExitCode) -> Self { - match val { - wasi::ExitCode::Errno(errno) => JournalExitCodeV1::Errno(errno as u16), - wasi::ExitCode::Other(id) => JournalExitCodeV1::Other(id), - } - } -} - -impl From for wasi::ExitCode { - fn from(val: JournalExitCodeV1) -> Self { - match val { - JournalExitCodeV1::Errno(errno) => { - wasi::ExitCode::Errno(errno.try_into().unwrap_or(wasi::Errno::Unknown)) - } - JournalExitCodeV1::Other(id) => wasi::ExitCode::Other(id), - } - } -} - -impl From<&'_ ArchivedJournalExitCodeV1> for wasi::ExitCode { - fn from(val: &'_ ArchivedJournalExitCodeV1) -> Self { - match val { - ArchivedJournalExitCodeV1::Errno(errno) => { - wasi::ExitCode::Errno((*errno).try_into().unwrap_or(wasi::Errno::Unknown)) - } - ArchivedJournalExitCodeV1::Other(id) => wasi::ExitCode::Other(*id), - } - } -} - -#[repr(C)] -#[derive( - Debug, - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - RkyvSerialize, - RkyvDeserialize, - Archive, -)] -#[archive_attr(derive(CheckBytes))] -pub enum JournalSnapshotTriggerV1 { - Idle, - Listen, - Environ, - Stdin, - Timer, - Sigint, - Sigalrm, - Sigtstp, - Sigstop, - NonDeterministicCall, -} - -impl From for JournalSnapshotTriggerV1 { - fn from(val: SnapshotTrigger) -> Self { - match val { - SnapshotTrigger::Idle => JournalSnapshotTriggerV1::Idle, - SnapshotTrigger::FirstListen => JournalSnapshotTriggerV1::Listen, - SnapshotTrigger::FirstEnviron => JournalSnapshotTriggerV1::Environ, - SnapshotTrigger::FirstStdin => JournalSnapshotTriggerV1::Stdin, - SnapshotTrigger::PeriodicInterval => JournalSnapshotTriggerV1::Timer, - SnapshotTrigger::Sigint => JournalSnapshotTriggerV1::Sigint, - SnapshotTrigger::Sigalrm => JournalSnapshotTriggerV1::Sigalrm, - SnapshotTrigger::Sigtstp => JournalSnapshotTriggerV1::Sigtstp, - SnapshotTrigger::Sigstop => JournalSnapshotTriggerV1::Sigstop, - SnapshotTrigger::NonDeterministicCall => JournalSnapshotTriggerV1::NonDeterministicCall, - } - } -} - -impl From for SnapshotTrigger { - fn from(val: JournalSnapshotTriggerV1) -> Self { - match val { - JournalSnapshotTriggerV1::Idle => SnapshotTrigger::Idle, - JournalSnapshotTriggerV1::Listen => SnapshotTrigger::FirstListen, - JournalSnapshotTriggerV1::Environ => SnapshotTrigger::FirstEnviron, - JournalSnapshotTriggerV1::Stdin => SnapshotTrigger::FirstStdin, - JournalSnapshotTriggerV1::Timer => SnapshotTrigger::PeriodicInterval, - JournalSnapshotTriggerV1::Sigint => SnapshotTrigger::Sigint, - JournalSnapshotTriggerV1::Sigalrm => SnapshotTrigger::Sigalrm, - JournalSnapshotTriggerV1::Sigtstp => SnapshotTrigger::Sigtstp, - JournalSnapshotTriggerV1::Sigstop => SnapshotTrigger::Sigstop, - JournalSnapshotTriggerV1::NonDeterministicCall => SnapshotTrigger::NonDeterministicCall, - } - } -} - -impl From<&'_ ArchivedJournalSnapshotTriggerV1> for SnapshotTrigger { - fn from(val: &'_ ArchivedJournalSnapshotTriggerV1) -> Self { - match val { - ArchivedJournalSnapshotTriggerV1::Idle => SnapshotTrigger::Idle, - ArchivedJournalSnapshotTriggerV1::Listen => SnapshotTrigger::FirstListen, - ArchivedJournalSnapshotTriggerV1::Environ => SnapshotTrigger::FirstEnviron, - ArchivedJournalSnapshotTriggerV1::Stdin => SnapshotTrigger::FirstStdin, - ArchivedJournalSnapshotTriggerV1::Timer => SnapshotTrigger::PeriodicInterval, - ArchivedJournalSnapshotTriggerV1::Sigint => SnapshotTrigger::Sigint, - ArchivedJournalSnapshotTriggerV1::Sigalrm => SnapshotTrigger::Sigalrm, - ArchivedJournalSnapshotTriggerV1::Sigtstp => SnapshotTrigger::Sigtstp, - ArchivedJournalSnapshotTriggerV1::Sigstop => SnapshotTrigger::Sigstop, - ArchivedJournalSnapshotTriggerV1::NonDeterministicCall => { - SnapshotTrigger::NonDeterministicCall - } - } - } -} - -#[repr(C)] -#[derive( - Debug, - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - RkyvSerialize, - RkyvDeserialize, - Archive, -)] -#[archive_attr(derive(CheckBytes))] -pub enum JournalEpollCtlV1 { - Add, - Mod, - Del, - Unknown, -} - -impl From for JournalEpollCtlV1 { - fn from(val: wasi::EpollCtl) -> Self { - match val { - wasi::EpollCtl::Add => JournalEpollCtlV1::Add, - wasi::EpollCtl::Mod => JournalEpollCtlV1::Mod, - wasi::EpollCtl::Del => JournalEpollCtlV1::Del, - wasi::EpollCtl::Unknown => JournalEpollCtlV1::Unknown, - } - } -} - -impl From for wasi::EpollCtl { - fn from(val: JournalEpollCtlV1) -> Self { - match val { - JournalEpollCtlV1::Add => wasi::EpollCtl::Add, - JournalEpollCtlV1::Mod => wasi::EpollCtl::Mod, - JournalEpollCtlV1::Del => wasi::EpollCtl::Del, - JournalEpollCtlV1::Unknown => wasi::EpollCtl::Unknown, - } - } -} - -impl From<&'_ ArchivedJournalEpollCtlV1> for wasi::EpollCtl { - fn from(val: &'_ ArchivedJournalEpollCtlV1) -> Self { - match val { - ArchivedJournalEpollCtlV1::Add => wasi::EpollCtl::Add, - ArchivedJournalEpollCtlV1::Mod => wasi::EpollCtl::Mod, - ArchivedJournalEpollCtlV1::Del => wasi::EpollCtl::Del, - ArchivedJournalEpollCtlV1::Unknown => wasi::EpollCtl::Unknown, - } - } -} - -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] -#[archive_attr(derive(CheckBytes))] -pub struct JournalEpollEventCtlV1 { - pub events: u32, - pub ptr: u64, - pub fd: u32, - pub data1: u32, - pub data2: u64, -} - -impl From for JournalEpollEventCtlV1 { - fn from(val: EpollEventCtl) -> Self { - JournalEpollEventCtlV1 { - events: val.events.bits(), - ptr: val.ptr, - fd: val.fd, - data1: val.data1, - data2: val.data2, - } - } -} - -impl From for EpollEventCtl { - fn from(val: JournalEpollEventCtlV1) -> Self { - Self { - events: EpollType::from_bits_truncate(val.events), - ptr: val.ptr, - fd: val.fd, - data1: val.data1, - data2: val.data2, - } - } -} - -impl From<&'_ ArchivedJournalEpollEventCtlV1> for EpollEventCtl { - fn from(val: &'_ ArchivedJournalEpollEventCtlV1) -> Self { - Self { - events: EpollType::from_bits_truncate(val.events), - ptr: val.ptr, - fd: val.fd, - data1: val.data1, - data2: val.data2, - } - } -} - -#[repr(C)] -#[derive( - Debug, - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - RkyvSerialize, - RkyvDeserialize, - Archive, -)] -#[archive_attr(derive(CheckBytes))] -pub enum JournalStreamSecurityV1 { - Unencrypted, - AnyEncryption, - ClassicEncryption, - DoubleEncryption, - Unknown, -} - -impl From for JournalStreamSecurityV1 { - fn from(val: StreamSecurity) -> Self { - match val { - StreamSecurity::Unencrypted => JournalStreamSecurityV1::Unencrypted, - StreamSecurity::AnyEncyption => JournalStreamSecurityV1::AnyEncryption, - StreamSecurity::ClassicEncryption => JournalStreamSecurityV1::ClassicEncryption, - StreamSecurity::DoubleEncryption => JournalStreamSecurityV1::DoubleEncryption, - } - } -} - -impl From for StreamSecurity { - fn from(val: JournalStreamSecurityV1) -> Self { - match val { - JournalStreamSecurityV1::Unencrypted => StreamSecurity::Unencrypted, - JournalStreamSecurityV1::AnyEncryption => StreamSecurity::AnyEncyption, - JournalStreamSecurityV1::ClassicEncryption => StreamSecurity::ClassicEncryption, - JournalStreamSecurityV1::DoubleEncryption => StreamSecurity::DoubleEncryption, - JournalStreamSecurityV1::Unknown => StreamSecurity::AnyEncyption, - } - } -} - -impl From<&'_ ArchivedJournalStreamSecurityV1> for StreamSecurity { - fn from(val: &'_ ArchivedJournalStreamSecurityV1) -> Self { - match val { - ArchivedJournalStreamSecurityV1::Unencrypted => StreamSecurity::Unencrypted, - ArchivedJournalStreamSecurityV1::AnyEncryption => StreamSecurity::AnyEncyption, - ArchivedJournalStreamSecurityV1::ClassicEncryption => StreamSecurity::ClassicEncryption, - ArchivedJournalStreamSecurityV1::DoubleEncryption => StreamSecurity::DoubleEncryption, - ArchivedJournalStreamSecurityV1::Unknown => StreamSecurity::AnyEncyption, - } - } -} - -#[repr(C)] -#[derive( - Debug, - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - RkyvSerialize, - RkyvDeserialize, - Archive, -)] -#[archive_attr(derive(CheckBytes))] -pub enum JournalAddressfamilyV1 { - Unspec, - Inet4, - Inet6, - Unix, -} - -impl From for JournalAddressfamilyV1 { - fn from(val: wasi::Addressfamily) -> Self { - match val { - wasi::Addressfamily::Unspec => JournalAddressfamilyV1::Unspec, - wasi::Addressfamily::Inet4 => JournalAddressfamilyV1::Inet4, - wasi::Addressfamily::Inet6 => JournalAddressfamilyV1::Inet6, - wasi::Addressfamily::Unix => JournalAddressfamilyV1::Unix, - } - } -} - -impl From for wasi::Addressfamily { - fn from(val: JournalAddressfamilyV1) -> Self { - match val { - JournalAddressfamilyV1::Unspec => wasi::Addressfamily::Unspec, - JournalAddressfamilyV1::Inet4 => wasi::Addressfamily::Inet4, - JournalAddressfamilyV1::Inet6 => wasi::Addressfamily::Inet6, - JournalAddressfamilyV1::Unix => wasi::Addressfamily::Unix, - } - } -} - -impl From<&'_ ArchivedJournalAddressfamilyV1> for wasi::Addressfamily { - fn from(val: &'_ ArchivedJournalAddressfamilyV1) -> Self { - match val { - ArchivedJournalAddressfamilyV1::Unspec => wasi::Addressfamily::Unspec, - ArchivedJournalAddressfamilyV1::Inet4 => wasi::Addressfamily::Inet4, - ArchivedJournalAddressfamilyV1::Inet6 => wasi::Addressfamily::Inet6, - ArchivedJournalAddressfamilyV1::Unix => wasi::Addressfamily::Unix, - } - } -} - -#[repr(C)] -#[derive( - Debug, - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - RkyvSerialize, - RkyvDeserialize, - Archive, -)] -#[archive_attr(derive(CheckBytes))] -pub enum JournalSocktypeV1 { - Unknown, - Stream, - Dgram, - Raw, - Seqpacket, -} - -impl From for JournalSocktypeV1 { - fn from(val: wasi::Socktype) -> Self { - match val { - wasi::Socktype::Stream => JournalSocktypeV1::Stream, - wasi::Socktype::Dgram => JournalSocktypeV1::Dgram, - wasi::Socktype::Raw => JournalSocktypeV1::Raw, - wasi::Socktype::Seqpacket => JournalSocktypeV1::Seqpacket, - wasi::Socktype::Unknown => JournalSocktypeV1::Unknown, - } - } -} - -impl From for wasi::Socktype { - fn from(val: JournalSocktypeV1) -> Self { - match val { - JournalSocktypeV1::Stream => wasi::Socktype::Stream, - JournalSocktypeV1::Dgram => wasi::Socktype::Dgram, - JournalSocktypeV1::Raw => wasi::Socktype::Raw, - JournalSocktypeV1::Seqpacket => wasi::Socktype::Seqpacket, - JournalSocktypeV1::Unknown => wasi::Socktype::Unknown, - } - } -} - -impl From<&'_ ArchivedJournalSocktypeV1> for wasi::Socktype { - fn from(val: &'_ ArchivedJournalSocktypeV1) -> Self { - match val { - ArchivedJournalSocktypeV1::Stream => wasi::Socktype::Stream, - ArchivedJournalSocktypeV1::Dgram => wasi::Socktype::Dgram, - ArchivedJournalSocktypeV1::Raw => wasi::Socktype::Raw, - ArchivedJournalSocktypeV1::Seqpacket => wasi::Socktype::Seqpacket, - ArchivedJournalSocktypeV1::Unknown => wasi::Socktype::Unknown, - } - } -} - -#[repr(C)] -#[derive( - Debug, - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - RkyvSerialize, - RkyvDeserialize, - Archive, -)] -#[archive_attr(derive(CheckBytes))] -pub enum JournalSockoptionV1 { - Noop, - ReusePort, - ReuseAddr, - NoDelay, - DontRoute, - OnlyV6, - Broadcast, - MulticastLoopV4, - MulticastLoopV6, - Promiscuous, - Listening, - LastError, - KeepAlive, - Linger, - OobInline, - RecvBufSize, - SendBufSize, - RecvLowat, - SendLowat, - RecvTimeout, - SendTimeout, - ConnectTimeout, - AcceptTimeout, - Ttl, - MulticastTtlV4, - Type, - Proto, -} - -impl From for JournalSockoptionV1 { - fn from(val: wasi::Sockoption) -> Self { - match val { - wasi::Sockoption::Noop => JournalSockoptionV1::Noop, - wasi::Sockoption::ReusePort => JournalSockoptionV1::ReusePort, - wasi::Sockoption::ReuseAddr => JournalSockoptionV1::ReuseAddr, - wasi::Sockoption::NoDelay => JournalSockoptionV1::NoDelay, - wasi::Sockoption::DontRoute => JournalSockoptionV1::DontRoute, - wasi::Sockoption::OnlyV6 => JournalSockoptionV1::OnlyV6, - wasi::Sockoption::Broadcast => JournalSockoptionV1::Broadcast, - wasi::Sockoption::MulticastLoopV4 => JournalSockoptionV1::MulticastLoopV4, - wasi::Sockoption::MulticastLoopV6 => JournalSockoptionV1::MulticastLoopV6, - wasi::Sockoption::Promiscuous => JournalSockoptionV1::Promiscuous, - wasi::Sockoption::Listening => JournalSockoptionV1::Listening, - wasi::Sockoption::LastError => JournalSockoptionV1::LastError, - wasi::Sockoption::KeepAlive => JournalSockoptionV1::KeepAlive, - wasi::Sockoption::Linger => JournalSockoptionV1::Linger, - wasi::Sockoption::OobInline => JournalSockoptionV1::OobInline, - wasi::Sockoption::RecvBufSize => JournalSockoptionV1::RecvBufSize, - wasi::Sockoption::SendBufSize => JournalSockoptionV1::SendBufSize, - wasi::Sockoption::RecvLowat => JournalSockoptionV1::RecvLowat, - wasi::Sockoption::SendLowat => JournalSockoptionV1::SendLowat, - wasi::Sockoption::RecvTimeout => JournalSockoptionV1::RecvTimeout, - wasi::Sockoption::SendTimeout => JournalSockoptionV1::SendTimeout, - wasi::Sockoption::ConnectTimeout => JournalSockoptionV1::ConnectTimeout, - wasi::Sockoption::AcceptTimeout => JournalSockoptionV1::AcceptTimeout, - wasi::Sockoption::Ttl => JournalSockoptionV1::Ttl, - wasi::Sockoption::MulticastTtlV4 => JournalSockoptionV1::MulticastTtlV4, - wasi::Sockoption::Type => JournalSockoptionV1::Type, - wasi::Sockoption::Proto => JournalSockoptionV1::Proto, - } - } -} - -impl From for wasi::Sockoption { - fn from(val: JournalSockoptionV1) -> Self { - match val { - JournalSockoptionV1::Noop => wasi::Sockoption::Noop, - JournalSockoptionV1::ReusePort => wasi::Sockoption::ReusePort, - JournalSockoptionV1::ReuseAddr => wasi::Sockoption::ReuseAddr, - JournalSockoptionV1::NoDelay => wasi::Sockoption::NoDelay, - JournalSockoptionV1::DontRoute => wasi::Sockoption::DontRoute, - JournalSockoptionV1::OnlyV6 => wasi::Sockoption::OnlyV6, - JournalSockoptionV1::Broadcast => wasi::Sockoption::Broadcast, - JournalSockoptionV1::MulticastLoopV4 => wasi::Sockoption::MulticastLoopV4, - JournalSockoptionV1::MulticastLoopV6 => wasi::Sockoption::MulticastLoopV6, - JournalSockoptionV1::Promiscuous => wasi::Sockoption::Promiscuous, - JournalSockoptionV1::Listening => wasi::Sockoption::Listening, - JournalSockoptionV1::LastError => wasi::Sockoption::LastError, - JournalSockoptionV1::KeepAlive => wasi::Sockoption::KeepAlive, - JournalSockoptionV1::Linger => wasi::Sockoption::Linger, - JournalSockoptionV1::OobInline => wasi::Sockoption::OobInline, - JournalSockoptionV1::RecvBufSize => wasi::Sockoption::RecvBufSize, - JournalSockoptionV1::SendBufSize => wasi::Sockoption::SendBufSize, - JournalSockoptionV1::RecvLowat => wasi::Sockoption::RecvLowat, - JournalSockoptionV1::SendLowat => wasi::Sockoption::SendLowat, - JournalSockoptionV1::RecvTimeout => wasi::Sockoption::RecvTimeout, - JournalSockoptionV1::SendTimeout => wasi::Sockoption::SendTimeout, - JournalSockoptionV1::ConnectTimeout => wasi::Sockoption::ConnectTimeout, - JournalSockoptionV1::AcceptTimeout => wasi::Sockoption::AcceptTimeout, - JournalSockoptionV1::Ttl => wasi::Sockoption::Ttl, - JournalSockoptionV1::MulticastTtlV4 => wasi::Sockoption::MulticastTtlV4, - JournalSockoptionV1::Type => wasi::Sockoption::Type, - JournalSockoptionV1::Proto => wasi::Sockoption::Proto, - } - } -} - -impl From<&'_ ArchivedJournalSockoptionV1> for wasi::Sockoption { - fn from(val: &'_ ArchivedJournalSockoptionV1) -> Self { - match val { - ArchivedJournalSockoptionV1::Noop => wasi::Sockoption::Noop, - ArchivedJournalSockoptionV1::ReusePort => wasi::Sockoption::ReusePort, - ArchivedJournalSockoptionV1::ReuseAddr => wasi::Sockoption::ReuseAddr, - ArchivedJournalSockoptionV1::NoDelay => wasi::Sockoption::NoDelay, - ArchivedJournalSockoptionV1::DontRoute => wasi::Sockoption::DontRoute, - ArchivedJournalSockoptionV1::OnlyV6 => wasi::Sockoption::OnlyV6, - ArchivedJournalSockoptionV1::Broadcast => wasi::Sockoption::Broadcast, - ArchivedJournalSockoptionV1::MulticastLoopV4 => wasi::Sockoption::MulticastLoopV4, - ArchivedJournalSockoptionV1::MulticastLoopV6 => wasi::Sockoption::MulticastLoopV6, - ArchivedJournalSockoptionV1::Promiscuous => wasi::Sockoption::Promiscuous, - ArchivedJournalSockoptionV1::Listening => wasi::Sockoption::Listening, - ArchivedJournalSockoptionV1::LastError => wasi::Sockoption::LastError, - ArchivedJournalSockoptionV1::KeepAlive => wasi::Sockoption::KeepAlive, - ArchivedJournalSockoptionV1::Linger => wasi::Sockoption::Linger, - ArchivedJournalSockoptionV1::OobInline => wasi::Sockoption::OobInline, - ArchivedJournalSockoptionV1::RecvBufSize => wasi::Sockoption::RecvBufSize, - ArchivedJournalSockoptionV1::SendBufSize => wasi::Sockoption::SendBufSize, - ArchivedJournalSockoptionV1::RecvLowat => wasi::Sockoption::RecvLowat, - ArchivedJournalSockoptionV1::SendLowat => wasi::Sockoption::SendLowat, - ArchivedJournalSockoptionV1::RecvTimeout => wasi::Sockoption::RecvTimeout, - ArchivedJournalSockoptionV1::SendTimeout => wasi::Sockoption::SendTimeout, - ArchivedJournalSockoptionV1::ConnectTimeout => wasi::Sockoption::ConnectTimeout, - ArchivedJournalSockoptionV1::AcceptTimeout => wasi::Sockoption::AcceptTimeout, - ArchivedJournalSockoptionV1::Ttl => wasi::Sockoption::Ttl, - ArchivedJournalSockoptionV1::MulticastTtlV4 => wasi::Sockoption::MulticastTtlV4, - ArchivedJournalSockoptionV1::Type => wasi::Sockoption::Type, - ArchivedJournalSockoptionV1::Proto => wasi::Sockoption::Proto, - } - } -} - -#[repr(C)] -#[derive( - Debug, - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - RkyvSerialize, - RkyvDeserialize, - Archive, -)] -#[archive_attr(derive(CheckBytes))] -pub enum JournalTimeTypeV1 { - ReadTimeout, - WriteTimeout, - AcceptTimeout, - ConnectTimeout, - BindTimeout, - Linger, -} - -impl From for JournalTimeTypeV1 { - fn from(val: SocketOptTimeType) -> Self { - match val { - SocketOptTimeType::ReadTimeout => JournalTimeTypeV1::ReadTimeout, - SocketOptTimeType::WriteTimeout => JournalTimeTypeV1::WriteTimeout, - SocketOptTimeType::AcceptTimeout => JournalTimeTypeV1::AcceptTimeout, - SocketOptTimeType::ConnectTimeout => JournalTimeTypeV1::ConnectTimeout, - SocketOptTimeType::BindTimeout => JournalTimeTypeV1::BindTimeout, - SocketOptTimeType::Linger => JournalTimeTypeV1::Linger, - } - } -} - -impl From for SocketOptTimeType { - fn from(val: JournalTimeTypeV1) -> Self { - match val { - JournalTimeTypeV1::ReadTimeout => SocketOptTimeType::ReadTimeout, - JournalTimeTypeV1::WriteTimeout => SocketOptTimeType::WriteTimeout, - JournalTimeTypeV1::AcceptTimeout => SocketOptTimeType::AcceptTimeout, - JournalTimeTypeV1::ConnectTimeout => SocketOptTimeType::ConnectTimeout, - JournalTimeTypeV1::BindTimeout => SocketOptTimeType::BindTimeout, - JournalTimeTypeV1::Linger => SocketOptTimeType::Linger, - } - } -} - -impl From<&'_ ArchivedJournalTimeTypeV1> for SocketOptTimeType { - fn from(val: &'_ ArchivedJournalTimeTypeV1) -> Self { - match val { - ArchivedJournalTimeTypeV1::ReadTimeout => SocketOptTimeType::ReadTimeout, - ArchivedJournalTimeTypeV1::WriteTimeout => SocketOptTimeType::WriteTimeout, - ArchivedJournalTimeTypeV1::AcceptTimeout => SocketOptTimeType::AcceptTimeout, - ArchivedJournalTimeTypeV1::ConnectTimeout => SocketOptTimeType::ConnectTimeout, - ArchivedJournalTimeTypeV1::BindTimeout => SocketOptTimeType::BindTimeout, - ArchivedJournalTimeTypeV1::Linger => SocketOptTimeType::Linger, - } - } -} - -#[repr(C)] -#[derive( - Debug, - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - RkyvSerialize, - RkyvDeserialize, - Archive, -)] -#[archive_attr(derive(CheckBytes))] -pub enum JournalSocketShutdownV1 { - Read, - Write, - Both, -} - -impl From for JournalSocketShutdownV1 { - fn from(val: SocketShutdownHow) -> Self { - match val { - SocketShutdownHow::Read => JournalSocketShutdownV1::Read, - SocketShutdownHow::Write => JournalSocketShutdownV1::Write, - SocketShutdownHow::Both => JournalSocketShutdownV1::Both, - } - } -} - -impl From for SocketShutdownHow { - fn from(val: JournalSocketShutdownV1) -> Self { - match val { - JournalSocketShutdownV1::Read => SocketShutdownHow::Read, - JournalSocketShutdownV1::Write => SocketShutdownHow::Write, - JournalSocketShutdownV1::Both => SocketShutdownHow::Both, - } - } -} - -impl From<&'_ ArchivedJournalSocketShutdownV1> for SocketShutdownHow { - fn from(val: &'_ ArchivedJournalSocketShutdownV1) -> Self { - match val { - ArchivedJournalSocketShutdownV1::Read => SocketShutdownHow::Read, - ArchivedJournalSocketShutdownV1::Write => SocketShutdownHow::Write, - ArchivedJournalSocketShutdownV1::Both => SocketShutdownHow::Both, - } - } -} - -impl<'a> TryFrom> for JournalEntry<'a> { - type Error = anyhow::Error; - - fn try_from(value: ArchivedJournalEntry<'a>) -> anyhow::Result { - Ok(match value { - ArchivedJournalEntry::InitModuleV1(ArchivedJournalEntryInitModuleV1 { wasm_hash }) => { - Self::InitModuleV1 { - wasm_hash: *wasm_hash, - } - } - ArchivedJournalEntry::UpdateMemoryRegionV1( - ArchivedJournalEntryUpdateMemoryRegionV1 { - start, - end, - compressed_data, - }, - ) => Self::UpdateMemoryRegionV1 { - region: (*start)..(*end), - data: Cow::Owned(decompress_size_prepended(compressed_data.as_ref())?), - }, - ArchivedJournalEntry::ProcessExitV1(ArchivedJournalEntryProcessExitV1 { - exit_code, - }) => Self::ProcessExitV1 { - exit_code: exit_code.as_ref().map(|code| code.into()), - }, - ArchivedJournalEntry::SetThreadV1(ArchivedJournalEntrySetThreadV1 { - id, - call_stack, - memory_stack, - store_data, - is_64bit, - }) => Self::SetThreadV1 { - id: (*id).into(), - call_stack: call_stack.as_ref().into(), - memory_stack: memory_stack.as_ref().into(), - store_data: store_data.as_ref().into(), - is_64bit: *is_64bit, - }, - ArchivedJournalEntry::CloseThreadV1(ArchivedJournalEntryCloseThreadV1 { - id, - exit_code, - }) => Self::CloseThreadV1 { - id: (*id).into(), - exit_code: exit_code.as_ref().map(|code| code.into()), - }, - ArchivedJournalEntry::FileDescriptorWriteV1( - ArchivedJournalEntryFileDescriptorWriteV1 { - data, - fd, - offset, - is_64bit, - }, - ) => Self::FileDescriptorWriteV1 { - data: data.as_ref().into(), - fd: *fd, - offset: *offset, - is_64bit: *is_64bit, - }, - ArchivedJournalEntry::FileDescriptorSeekV1( - ArchivedJournalEntryFileDescriptorSeekV1 { - fd, - offset, - ref whence, - }, - ) => Self::FileDescriptorSeekV1 { - fd: *fd, - offset: *offset, - whence: whence.into(), - }, - ArchivedJournalEntry::OpenFileDescriptorV1( - ArchivedJournalEntryOpenFileDescriptorV1 { - fd, - dirfd, - dirflags, - path, - o_flags, - fs_rights_base, - fs_rights_inheriting, - fs_flags, - }, - ) => Self::OpenFileDescriptorV1 { - fd: *fd, - dirfd: *dirfd, - dirflags: *dirflags, - path: path.as_ref().into(), - o_flags: wasi::Oflags::from_bits_truncate(*o_flags), - fs_rights_base: wasi::Rights::from_bits_truncate(*fs_rights_base), - fs_rights_inheriting: wasi::Rights::from_bits_truncate(*fs_rights_inheriting), - fs_flags: wasi::Fdflags::from_bits_truncate(*fs_flags), - }, - ArchivedJournalEntry::CloseFileDescriptorV1( - ArchivedJournalEntryCloseFileDescriptorV1 { fd }, - ) => Self::CloseFileDescriptorV1 { fd: *fd }, - ArchivedJournalEntry::RemoveDirectoryV1(ArchivedJournalEntryRemoveDirectoryV1 { - fd, - path, - }) => Self::RemoveDirectoryV1 { - fd: *fd, - path: path.as_ref().into(), - }, - ArchivedJournalEntry::UnlinkFileV1(ArchivedJournalEntryUnlinkFileV1 { fd, path }) => { - Self::UnlinkFileV1 { - fd: *fd, - path: path.as_ref().into(), - } - } - ArchivedJournalEntry::PathRenameV1(ArchivedJournalEntryPathRenameV1 { - old_fd, - old_path, - new_fd, - new_path, - }) => Self::PathRenameV1 { - old_fd: *old_fd, - old_path: old_path.as_ref().into(), - new_fd: *new_fd, - new_path: new_path.as_ref().into(), - }, - ArchivedJournalEntry::SnapshotV1(ArchivedJournalEntrySnapshotV1 { - since_epoch, - ref trigger, - }) => Self::SnapshotV1 { - when: SystemTime::UNIX_EPOCH - .checked_add((*since_epoch).try_into().unwrap()) - .unwrap_or(SystemTime::UNIX_EPOCH), - trigger: trigger.into(), - }, - ArchivedJournalEntry::SetClockTimeV1(ArchivedJournalEntrySetClockTimeV1 { - ref clock_id, - time, - }) => Self::SetClockTimeV1 { - clock_id: clock_id.into(), - time: *time, - }, - ArchivedJournalEntry::RenumberFileDescriptorV1( - ArchivedJournalEntryRenumberFileDescriptorV1 { old_fd, new_fd }, - ) => Self::RenumberFileDescriptorV1 { - old_fd: *old_fd, - new_fd: *new_fd, - }, - ArchivedJournalEntry::DuplicateFileDescriptorV1( - ArchivedJournalEntryDuplicateFileDescriptorV1 { - original_fd: old_fd, - copied_fd: new_fd, - }, - ) => Self::DuplicateFileDescriptorV1 { - original_fd: *old_fd, - copied_fd: *new_fd, - }, - ArchivedJournalEntry::CreateDirectoryV1(ArchivedJournalEntryCreateDirectoryV1 { - fd, - path, - }) => Self::CreateDirectoryV1 { - fd: *fd, - path: path.as_ref().into(), - }, - ArchivedJournalEntry::PathSetTimesV1(ArchivedJournalEntryPathSetTimesV1 { - fd, - path, - flags, - st_atim, - st_mtim, - fst_flags, - }) => Self::PathSetTimesV1 { - fd: *fd, - path: path.as_ref().into(), - flags: *flags, - st_atim: *st_atim, - st_mtim: *st_mtim, - fst_flags: wasi::Fstflags::from_bits_truncate(*fst_flags), - }, - ArchivedJournalEntry::FileDescriptorSetTimesV1( - ArchivedJournalEntryFileDescriptorSetTimesV1 { - fd, - st_atim, - st_mtim, - fst_flags, - }, - ) => Self::FileDescriptorSetTimesV1 { - fd: *fd, - st_atim: *st_atim, - st_mtim: *st_mtim, - fst_flags: wasi::Fstflags::from_bits_truncate(*fst_flags), - }, - ArchivedJournalEntry::FileDescriptorSetSizeV1( - ArchivedJournalEntryFileDescriptorSetSizeV1 { fd, st_size }, - ) => Self::FileDescriptorSetSizeV1 { - fd: *fd, - st_size: *st_size, - }, - ArchivedJournalEntry::FileDescriptorSetFlagsV1( - ArchivedJournalEntryFileDescriptorSetFlagsV1 { fd, flags }, - ) => Self::FileDescriptorSetFlagsV1 { - fd: *fd, - flags: Fdflags::from_bits_truncate(*flags), - }, - ArchivedJournalEntry::FileDescriptorSetRightsV1( - ArchivedJournalEntryFileDescriptorSetRightsV1 { - fd, - fs_rights_base, - fs_rights_inheriting, - }, - ) => Self::FileDescriptorSetRightsV1 { - fd: *fd, - fs_rights_base: Rights::from_bits_truncate(*fs_rights_base), - fs_rights_inheriting: Rights::from_bits_truncate(*fs_rights_inheriting), - }, - ArchivedJournalEntry::FileDescriptorAdviseV1( - ArchivedJournalEntryFileDescriptorAdviseV1 { - fd, - offset, - len, - ref advice, - }, - ) => Self::FileDescriptorAdviseV1 { - fd: *fd, - offset: *offset, - len: *len, - advice: advice.into(), - }, - ArchivedJournalEntry::FileDescriptorAllocateV1( - ArchivedJournalEntryFileDescriptorAllocateV1 { fd, offset, len }, - ) => Self::FileDescriptorAllocateV1 { - fd: *fd, - offset: *offset, - len: *len, - }, - ArchivedJournalEntry::CreateHardLinkV1(ArchivedJournalEntryCreateHardLinkV1 { - old_fd, - old_path, - old_flags, - new_fd, - new_path, - }) => Self::CreateHardLinkV1 { - old_fd: *old_fd, - old_path: old_path.as_ref().into(), - old_flags: *old_flags, - new_fd: *new_fd, - new_path: new_path.as_ref().into(), - }, - ArchivedJournalEntry::CreateSymbolicLinkV1( - ArchivedJournalEntryCreateSymbolicLinkV1 { - old_path, - fd, - new_path, - }, - ) => Self::CreateSymbolicLinkV1 { - old_path: old_path.as_ref().into(), - fd: *fd, - new_path: new_path.as_ref().into(), - }, - ArchivedJournalEntry::ChangeDirectoryV1(ArchivedJournalEntryChangeDirectoryV1 { - path, - }) => Self::ChangeDirectoryV1 { - path: path.as_ref().into(), - }, - ArchivedJournalEntry::EpollCreateV1(ArchivedJournalEntryEpollCreateV1 { fd }) => { - Self::EpollCreateV1 { fd: *fd } - } - ArchivedJournalEntry::EpollCtlV1(ArchivedJournalEntryEpollCtlV1 { - epfd, - ref op, - fd, - ref event, - }) => Self::EpollCtlV1 { - epfd: *epfd, - op: op.into(), - fd: *fd, - event: event.as_ref().map(|e| e.into()), - }, - ArchivedJournalEntry::TtySetV1(ArchivedJournalEntryTtySetV1 { - cols, - rows, - width, - height, - stdin_tty, - stdout_tty, - stderr_tty, - echo, - line_buffered, - line_feeds, - }) => Self::TtySetV1 { - tty: wasi::Tty { - cols: *cols, - rows: *rows, - width: *width, - height: *height, - stdin_tty: *stdin_tty, - stdout_tty: *stdout_tty, - stderr_tty: *stderr_tty, - echo: *echo, - line_buffered: *line_buffered, - }, - line_feeds: *line_feeds, - }, - ArchivedJournalEntry::CreatePipeV1(ArchivedJournalEntryCreatePipeV1 { fd1, fd2 }) => { - Self::CreatePipeV1 { - fd1: *fd1, - fd2: *fd2, - } - } - ArchivedJournalEntry::PortAddAddrV1(ArchivedJournalEntryPortAddAddrV1 { cidr }) => { - Self::PortAddAddrV1 { - cidr: IpCidr { - ip: cidr.ip.as_ipaddr(), - prefix: cidr.prefix, - }, - } - } - ArchivedJournalEntry::PortDelAddrV1(ArchivedJournalEntryPortDelAddrV1 { addr }) => { - Self::PortDelAddrV1 { - addr: addr.as_ipaddr(), - } - } - ArchivedJournalEntry::PortAddrClearV1 => Self::PortAddrClearV1, - ArchivedJournalEntry::PortBridgeV1(ArchivedJournalEntryPortBridgeV1 { - network, - token, - ref security, - }) => Self::PortBridgeV1 { - network: network.as_ref().into(), - token: token.as_ref().into(), - security: security.into(), - }, - ArchivedJournalEntry::PortUnbridgeV1 => Self::PortUnbridgeV1, - ArchivedJournalEntry::PortDhcpAcquireV1 => Self::PortDhcpAcquireV1, - ArchivedJournalEntry::PortGatewaySetV1(ArchivedJournalEntryPortGatewaySetV1 { ip }) => { - Self::PortGatewaySetV1 { ip: ip.as_ipaddr() } - } - ArchivedJournalEntry::PortRouteAddV1(ArchivedJournalEntryPortRouteAddV1 { - cidr, - via_router, - preferred_until, - expires_at, - }) => Self::PortRouteAddV1 { - cidr: IpCidr { - ip: cidr.ip.as_ipaddr(), - prefix: cidr.prefix, - }, - via_router: via_router.as_ipaddr(), - preferred_until: preferred_until - .as_ref() - .map(|time| (*time).try_into().unwrap()), - expires_at: expires_at.as_ref().map(|time| (*time).try_into().unwrap()), - }, - ArchivedJournalEntry::PortRouteClearV1 => Self::PortRouteClearV1, - ArchivedJournalEntry::PortRouteDelV1(ArchivedJournalEntryPortRouteDelV1 { ip }) => { - Self::PortRouteDelV1 { ip: ip.as_ipaddr() } - } - ArchivedJournalEntry::SocketOpenV1(ArchivedJournalEntrySocketOpenV1 { - ref af, - ref ty, - pt, - fd, - }) => Self::SocketOpenV1 { - af: af.into(), - ty: ty.into(), - pt: (*pt).try_into().unwrap_or(wasi::SockProto::Max), - fd: *fd, - }, - ArchivedJournalEntry::SocketListenV1(ArchivedJournalEntrySocketListenV1 { - fd, - backlog, - }) => Self::SocketListenV1 { - fd: *fd, - backlog: *backlog, - }, - ArchivedJournalEntry::SocketBindV1(ArchivedJournalEntrySocketBindV1 { fd, addr }) => { - Self::SocketBindV1 { - fd: *fd, - addr: addr.as_socket_addr(), - } - } - ArchivedJournalEntry::SocketConnectedV1(ArchivedJournalEntrySocketConnectedV1 { - fd, - addr, - }) => Self::SocketConnectedV1 { - fd: *fd, - addr: addr.as_socket_addr(), - }, - ArchivedJournalEntry::SocketAcceptedV1(ArchivedJournalEntrySocketAcceptedV1 { - listen_fd, - fd, - peer_addr, - fd_flags, - nonblocking, - }) => Self::SocketAcceptedV1 { - listen_fd: *listen_fd, - fd: *fd, - peer_addr: peer_addr.as_socket_addr(), - fd_flags: Fdflags::from_bits_truncate(*fd_flags), - non_blocking: *nonblocking, - }, - ArchivedJournalEntry::SocketJoinIpv4MulticastV1( - ArchivedJournalEntrySocketJoinIpv4MulticastV1 { - fd, - multiaddr, - iface, - }, - ) => Self::SocketJoinIpv4MulticastV1 { - fd: *fd, - multiaddr: multiaddr.as_ipv4(), - iface: iface.as_ipv4(), - }, - ArchivedJournalEntry::SocketJoinIpv6MulticastV1( - ArchivedJournalEntrySocketJoinIpv6MulticastV1 { - fd, - multiaddr, - iface, - }, - ) => Self::SocketJoinIpv6MulticastV1 { - fd: *fd, - multi_addr: multiaddr.as_ipv6(), - iface: *iface, - }, - ArchivedJournalEntry::SocketLeaveIpv4MulticastV1( - ArchivedJournalEntrySocketLeaveIpv4MulticastV1 { - fd, - multiaddr, - iface, - }, - ) => Self::SocketLeaveIpv4MulticastV1 { - fd: *fd, - multi_addr: multiaddr.as_ipv4(), - iface: iface.as_ipv4(), - }, - ArchivedJournalEntry::SocketLeaveIpv6MulticastV1( - ArchivedJournalEntrySocketLeaveIpv6MulticastV1 { - fd, - multiaddr, - iface, - }, - ) => Self::SocketLeaveIpv6MulticastV1 { - fd: *fd, - multi_addr: multiaddr.as_ipv6(), - iface: *iface, - }, - ArchivedJournalEntry::SocketSendFileV1(ArchivedJournalEntrySocketSendFileV1 { - socket_fd, - file_fd, - offset, - count, - }) => Self::SocketSendFileV1 { - socket_fd: *socket_fd, - file_fd: *file_fd, - offset: *offset, - count: *count, - }, - ArchivedJournalEntry::SocketSendToV1(ArchivedJournalEntrySocketSendToV1 { - fd, - data, - flags, - addr, - is_64bit, - }) => Self::SocketSendToV1 { - fd: *fd, - data: data.as_ref().into(), - flags: *flags, - addr: addr.as_socket_addr(), - is_64bit: *is_64bit, - }, - ArchivedJournalEntry::SocketSendV1(ArchivedJournalEntrySocketSendV1 { - fd, - data, - flags, - is_64bit, - }) => Self::SocketSendV1 { - fd: *fd, - data: data.as_ref().into(), - flags: *flags, - is_64bit: *is_64bit, - }, - ArchivedJournalEntry::SocketSetOptFlagV1(ArchivedJournalEntrySocketSetOptFlagV1 { - fd, - ref opt, - flag, - }) => Self::SocketSetOptFlagV1 { - fd: *fd, - opt: opt.into(), - flag: *flag, - }, - ArchivedJournalEntry::SocketSetOptSizeV1(ArchivedJournalEntrySocketSetOptSizeV1 { - fd, - ref opt, - size, - }) => Self::SocketSetOptSizeV1 { - fd: *fd, - opt: opt.into(), - size: *size, - }, - ArchivedJournalEntry::SocketSetOptTimeV1(ArchivedJournalEntrySocketSetOptTimeV1 { - fd, - ref ty, - time, - }) => Self::SocketSetOptTimeV1 { - fd: *fd, - ty: ty.into(), - time: time.as_ref().map(|time| (*time).try_into().unwrap()), - }, - ArchivedJournalEntry::SocketShutdownV1(ArchivedJournalEntrySocketShutdownV1 { - fd, - ref how, - }) => Self::SocketShutdownV1 { - fd: *fd, - how: how.into(), - }, - ArchivedJournalEntry::CreateEventV1(ArchivedJournalEntryCreateEventV1 { - initial_val, - flags, - fd, - }) => Self::CreateEventV1 { - initial_val: *initial_val, - flags: *flags, - fd: *fd, - }, - }) - } -} - -#[cfg(test)] -mod tests { - use rkyv::ser::serializers::{ - AllocScratch, CompositeSerializer, SharedSerializeMap, WriteSerializer, - }; - - use super::*; - - pub fn run_test<'a>(record: JournalEntry<'a>) { - tracing::info!("record: {:?}", record); - - // Determine the record type - let record_type = record.archive_record_type(); - tracing::info!("record_type: {:?}", record_type); - - // Serialize it - let mut buffer = Vec::new(); - let mut serializer = CompositeSerializer::new( - WriteSerializer::new(&mut buffer), - AllocScratch::default(), - SharedSerializeMap::default(), - ); - - record.clone().serialize_archive(&mut serializer).unwrap(); - let buffer = &buffer[..]; - if buffer.len() < 20 { - tracing::info!("buffer: {:x?}", buffer); - } else { - tracing::info!("buffer_len: {}", buffer.len()); - } - - // Deserialize it - let record2 = unsafe { record_type.deserialize_archive(buffer).unwrap() }; - tracing::info!("record2: {:?}", record2); - - // Check it - assert_eq!(record, record2); - - // Now make it static and check it again - let record3 = record2.into_owned(); - tracing::info!("record3: {:?}", record3); - assert_eq!(record, record3); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_init_module() { - run_test(JournalEntry::InitModuleV1 { - wasm_hash: [13u8; 8], - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_process_exit() { - run_test(JournalEntry::ProcessExitV1 { - exit_code: Some(wasi::ExitCode::Errno(wasi::Errno::Fault)), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_set_thread() { - run_test(JournalEntry::SetThreadV1 { - id: 1234u32.into(), - call_stack: vec![1, 2, 3].into(), - memory_stack: vec![4, 5, 6, 7].into(), - store_data: vec![10, 11].into(), - is_64bit: true, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_close_thread() { - run_test(JournalEntry::CloseThreadV1 { - id: 987u32.into(), - exit_code: Some(wasi::ExitCode::Errno(wasi::Errno::Fault)), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_descriptor_seek() { - run_test(JournalEntry::FileDescriptorSeekV1 { - fd: 765u32, - offset: 9183722450971234i64, - whence: wasi::Whence::End, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_descriptor_write() { - run_test(JournalEntry::FileDescriptorWriteV1 { - fd: 54321u32, - offset: 13897412934u64, - data: vec![74u8, 98u8, 36u8].into(), - is_64bit: true, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_update_memory() { - run_test(JournalEntry::UpdateMemoryRegionV1 { - region: 76u64..8237453u64, - data: [74u8; 40960].to_vec().into(), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_set_clock_time() { - run_test(JournalEntry::SetClockTimeV1 { - clock_id: wasi::Snapshot0Clockid::Realtime, - time: 7912837412934u64, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_open_file_descriptor() { - run_test(JournalEntry::OpenFileDescriptorV1 { - fd: 298745u32, - dirfd: 23458922u32, - dirflags: 134512345, - path: "/blah".into(), - o_flags: wasi::Oflags::all(), - fs_rights_base: wasi::Rights::all(), - fs_rights_inheriting: wasi::Rights::all(), - fs_flags: wasi::Fdflags::all(), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_close_descriptor() { - run_test(JournalEntry::CloseFileDescriptorV1 { fd: 23845732u32 }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_renumber_file_descriptor() { - run_test(JournalEntry::RenumberFileDescriptorV1 { - old_fd: 27834u32, - new_fd: 398452345u32, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_duplicate_file_descriptor() { - run_test(JournalEntry::DuplicateFileDescriptorV1 { - original_fd: 23482934u32, - copied_fd: 9384529u32, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_create_directory() { - run_test(JournalEntry::CreateDirectoryV1 { - fd: 238472u32, - path: "/joasjdf/asdfn".into(), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_remove_directory() { - run_test(JournalEntry::RemoveDirectoryV1 { - fd: 23894952u32, - path: "/blahblah".into(), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_path_set_times() { - run_test(JournalEntry::PathSetTimesV1 { - fd: 1238934u32, - flags: 234523, - path: "/".into(), - st_atim: 923452345, - st_mtim: 350, - fst_flags: wasi::Fstflags::all(), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_file_descriptor_set_times() { - run_test(JournalEntry::FileDescriptorSetTimesV1 { - fd: 898785u32, - st_atim: 29834952345, - st_mtim: 239845892345, - fst_flags: wasi::Fstflags::all(), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_file_descriptor_set_size() { - run_test(JournalEntry::FileDescriptorSetSizeV1 { - fd: 34958234u32, - st_size: 234958293845u64, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_file_descriptor_set_flags() { - run_test(JournalEntry::FileDescriptorSetFlagsV1 { - fd: 982348752u32, - flags: wasi::Fdflags::all(), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_file_descriptor_set_rights() { - run_test(JournalEntry::FileDescriptorSetRightsV1 { - fd: 872345u32, - fs_rights_base: wasi::Rights::all(), - fs_rights_inheriting: wasi::Rights::all(), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_file_descriptor_advise() { - run_test(JournalEntry::FileDescriptorAdviseV1 { - fd: 298434u32, - offset: 92834529092345, - len: 23485928345, - advice: wasi::Advice::Random, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_file_descriptor_allocate() { - run_test(JournalEntry::FileDescriptorAllocateV1 { - fd: 2934852, - offset: 23489582934523, - len: 9845982345, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_create_hard_link() { - run_test(JournalEntry::CreateHardLinkV1 { - old_fd: 324983845, - old_path: "/asjdfiasidfasdf".into(), - old_flags: 234857, - new_fd: 34958345, - new_path: "/ashdufnasd".into(), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_create_symbolic_link() { - run_test(JournalEntry::CreateSymbolicLinkV1 { - old_path: "/asjbndfjasdf/asdafasdf".into(), - fd: 235422345, - new_path: "/asdf".into(), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_unlink_file() { - run_test(JournalEntry::UnlinkFileV1 { - fd: 32452345, - path: "/asdfasd".into(), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_path_rename() { - run_test(JournalEntry::PathRenameV1 { - old_fd: 32451345, - old_path: "/asdfasdfas/asdfasdf".into(), - new_fd: 23452345, - new_path: "/ahgfdfghdfghdfgh".into(), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_change_directory() { - run_test(JournalEntry::ChangeDirectoryV1 { - path: "/etc".to_string().into(), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_epoll_create() { - run_test(JournalEntry::EpollCreateV1 { fd: 45384752 }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_epoll_ctl() { - run_test(JournalEntry::EpollCtlV1 { - epfd: 34523455, - op: wasi::EpollCtl::Unknown, - fd: 23452345, - event: Some(wasi::EpollEventCtl { - events: wasi::EpollType::all(), - ptr: 32452345, - fd: 23452345, - data1: 1235245756, - data2: 23452345, - }), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_tty_set() { - run_test(JournalEntry::TtySetV1 { - tty: wasi::Tty { - cols: 1234, - rows: 6754, - width: 4563456, - height: 345, - stdin_tty: true, - stdout_tty: false, - stderr_tty: true, - echo: true, - line_buffered: true, - }, - line_feeds: true, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_create_pipe() { - run_test(JournalEntry::CreatePipeV1 { - fd1: 3452345, - fd2: 2345163, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_create_event() { - run_test(JournalEntry::CreateEventV1 { - initial_val: 13451345, - flags: 2343, - fd: 5836544, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_port_add_addr() { - run_test(JournalEntry::PortAddAddrV1 { - cidr: IpCidr { - ip: Ipv4Addr::LOCALHOST.into(), - prefix: 24, - }, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_del_addr() { - run_test(JournalEntry::PortDelAddrV1 { - addr: Ipv6Addr::LOCALHOST.into(), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_addr_clear() { - run_test(JournalEntry::PortAddrClearV1); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_port_bridge() { - run_test(JournalEntry::PortBridgeV1 { - network: "mynetwork".into(), - token: format!("blh blah").into(), - security: StreamSecurity::ClassicEncryption, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_unbridge() { - run_test(JournalEntry::PortUnbridgeV1); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_dhcp_acquire() { - run_test(JournalEntry::PortDhcpAcquireV1); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_gateway_set() { - run_test(JournalEntry::PortGatewaySetV1 { - ip: Ipv4Addr::new(12, 34, 136, 220).into(), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_route_add() { - run_test(JournalEntry::PortRouteAddV1 { - cidr: IpCidr { - ip: Ipv4Addr::LOCALHOST.into(), - prefix: 24, - }, - via_router: Ipv4Addr::LOCALHOST.into(), - preferred_until: Some(Duration::MAX), - expires_at: Some(Duration::ZERO), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_route_clear() { - run_test(JournalEntry::PortRouteClearV1); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_route_del() { - run_test(JournalEntry::PortRouteDelV1 { - ip: Ipv4Addr::BROADCAST.into(), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_socket_open() { - run_test(JournalEntry::SocketOpenV1 { - af: wasi::Addressfamily::Inet6, - ty: wasi::Socktype::Stream, - pt: wasi::SockProto::Tcp, - fd: 23452345, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_socket_listen() { - run_test(JournalEntry::SocketListenV1 { - fd: 12341234, - backlog: 123, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_socket_bind() { - run_test(JournalEntry::SocketBindV1 { - fd: 2341234, - addr: SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 1234), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_socket_connected() { - run_test(JournalEntry::SocketConnectedV1 { - fd: 12341, - addr: SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 1234), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_socket_accepted() { - run_test(JournalEntry::SocketAcceptedV1 { - listen_fd: 21234, - fd: 1, - peer_addr: SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 3452), - fd_flags: wasi::Fdflags::all(), - non_blocking: true, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_socket_join_ipv4_multicast() { - run_test(JournalEntry::SocketJoinIpv4MulticastV1 { - fd: 12, - multiaddr: Ipv4Addr::new(123, 123, 123, 123).into(), - iface: Ipv4Addr::new(128, 0, 0, 1).into(), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_socket_join_ipv6_multicast() { - run_test(JournalEntry::SocketJoinIpv6MulticastV1 { - fd: 12, - multi_addr: Ipv6Addr::new(123, 123, 123, 123, 1234, 12663, 31, 1324).into(), - iface: 23541, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_socket_leave_ipv4_multicast() { - run_test(JournalEntry::SocketLeaveIpv4MulticastV1 { - fd: 12, - multi_addr: Ipv4Addr::new(123, 123, 123, 123).into(), - iface: Ipv4Addr::new(128, 0, 0, 1).into(), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_socket_leave_ipv6_multicast() { - run_test(JournalEntry::SocketLeaveIpv6MulticastV1 { - fd: 12, - multi_addr: Ipv6Addr::new(123, 123, 123, 123, 1234, 12663, 31, 1324).into(), - iface: 23541, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_socket_send_file() { - run_test(JournalEntry::SocketSendFileV1 { - socket_fd: 22234, - file_fd: 989, - offset: 124, - count: 345673456234651234, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_socket_send_to() { - run_test(JournalEntry::SocketSendToV1 { - fd: 123, - data: [98u8; 102400].to_vec().into(), - flags: 1234, - addr: SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 3452), - is_64bit: true, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_socket_send() { - run_test(JournalEntry::SocketSendV1 { - fd: 123, - data: [98u8; 102400].to_vec().into(), - flags: 1234, - is_64bit: true, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_socket_set_opt_flag() { - run_test(JournalEntry::SocketSetOptFlagV1 { - fd: 0, - opt: wasi::Sockoption::Linger, - flag: true, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_socket_set_opt_size() { - run_test(JournalEntry::SocketSetOptSizeV1 { - fd: 15, - opt: wasi::Sockoption::Linger, - size: 234234, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_socket_set_opt_time() { - run_test(JournalEntry::SocketSetOptTimeV1 { - fd: 0, - ty: SocketOptTimeType::AcceptTimeout, - time: Some(Duration::ZERO), - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_socket_shutdown() { - run_test(JournalEntry::SocketShutdownV1 { - fd: 123, - how: SocketShutdownHow::Both, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_snapshot() { - run_test(JournalEntry::SnapshotV1 { - when: SystemTime::now(), - trigger: SnapshotTrigger::Idle, - }); - } - - #[tracing_test::traced_test] - #[test] - pub fn test_record_alignment() { - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!( - std::mem::align_of::(), - 8 - ); - assert_eq!( - std::mem::align_of::(), - 8 - ); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!( - std::mem::align_of::(), - 8 - ); - assert_eq!( - std::mem::align_of::(), - 8 - ); - assert_eq!( - std::mem::align_of::(), - 8 - ); - assert_eq!( - std::mem::align_of::(), - 8 - ); - assert_eq!( - std::mem::align_of::(), - 8 - ); - assert_eq!( - std::mem::align_of::(), - 8 - ); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!( - std::mem::align_of::(), - 8 - ); - assert_eq!( - std::mem::align_of::(), - 8 - ); - assert_eq!( - std::mem::align_of::(), - 8 - ); - assert_eq!( - std::mem::align_of::(), - 8 - ); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - assert_eq!(std::mem::align_of::(), 8); - } -} diff --git a/lib/wasix/src/journal/effector/thread_state.rs b/lib/wasix/src/journal/effector/thread_state.rs index 93b4d3cf9fe..3f7b819b87d 100644 --- a/lib/wasix/src/journal/effector/thread_state.rs +++ b/lib/wasix/src/journal/effector/thread_state.rs @@ -1,3 +1,7 @@ +use wasmer_wasix_types::wasix::ThreadStartType; + +use crate::os::task::thread::WasiMemoryLayout; + use super::*; impl JournalEffector { @@ -7,6 +11,8 @@ impl JournalEffector { memory_stack: Bytes, rewind_stack: Bytes, store_data: Bytes, + start: ThreadStartType, + layout: WasiMemoryLayout, ) -> anyhow::Result<()> { Self::save_event( ctx, @@ -15,8 +21,35 @@ impl JournalEffector { call_stack: Cow::Owned(rewind_stack.into()), memory_stack: Cow::Owned(memory_stack.into()), store_data: Cow::Owned(store_data.into()), + start, + layout, is_64bit: M::is_64bit(), }, ) } + + pub fn apply_thread_state( + ctx: &mut FunctionEnvMut<'_, WasiEnv>, + id: WasiThreadId, + memory_stack: Bytes, + rewind_stack: Bytes, + store_data: Bytes, + start: ThreadStartType, + layout: WasiMemoryLayout, + ) -> anyhow::Result<()> { + let start_ptr: M::Offset = match start { + ThreadStartType::MainThread => { + return Err(anyhow::format_err!( + "unable to restore a main thread via this method" + )); + } + ThreadStartType::ThreadSpawn { start_ptr } => start_ptr + .try_into() + .map_err(|_| anyhow::format_err!("overflow while processing thread restoration"))?, + }; + + // Create the thread for this ID + //ctx.data().process.new_thread(layout, start); + Ok(()) + } } diff --git a/lib/wasix/src/os/task/process.rs b/lib/wasix/src/os/task/process.rs index 858d7de071e..0a870a12f85 100644 --- a/lib/wasix/src/os/task/process.rs +++ b/lib/wasix/src/os/task/process.rs @@ -20,6 +20,7 @@ use wasmer::FunctionEnvMut; use wasmer_wasix_types::{ types::Signal, wasi::{Errno, ExitCode, Snapshot0Clockid}, + wasix::ThreadStartType, }; use crate::{ @@ -214,6 +215,7 @@ impl WasiProcessInner { drop(guard); // Perform the unwind action + let thread_layout = ctx.data().thread.memory_layout().clone(); unwind::(ctx, move |mut ctx, memory_stack, rewind_stack| { // Grab all the globals and serialize them let store_data = crate::utils::store::capture_store_snapshot(&mut ctx.as_store_mut()) @@ -231,6 +233,7 @@ impl WasiProcessInner { ); // Write our thread state to the snapshot + let thread_start = ctx.data().thread.thread_start_type(); let tid = ctx.data().thread.tid(); if let Err(err) = JournalEffector::save_thread_state::( &mut ctx, @@ -238,6 +241,8 @@ impl WasiProcessInner { memory_stack.clone(), rewind_stack.clone(), store_data.clone(), + thread_start, + thread_layout, ) { return wasmer_types::OnCalledAction::Trap(err.into()); } @@ -373,6 +378,7 @@ impl WasiProcess { pub fn new_thread( &self, layout: WasiMemoryLayout, + start: ThreadStartType, ) -> Result { let control_plane = self.compute.must_upgrade(); let task_count_guard = control_plane.register_task()?; @@ -401,7 +407,15 @@ impl WasiProcess { }; // Insert the thread into the pool - let ctrl = WasiThread::new(self.pid(), tid, is_main, finished, task_count_guard, layout); + let ctrl = WasiThread::new( + self.pid(), + tid, + is_main, + finished, + task_count_guard, + layout, + start, + ); inner.threads.insert(tid, ctrl.clone()); inner.thread_count += 1; diff --git a/lib/wasix/src/os/task/thread.rs b/lib/wasix/src/os/task/thread.rs index e6de82e89b3..1751dbb9675 100644 --- a/lib/wasix/src/os/task/thread.rs +++ b/lib/wasix/src/os/task/thread.rs @@ -13,6 +13,7 @@ use wasmer::{ExportError, InstantiationError, MemoryError}; use wasmer_wasix_types::{ types::Signal, wasi::{Errno, ExitCode}, + wasix::ThreadStartType, }; use crate::{ @@ -100,6 +101,7 @@ pub struct ThreadStack { pub struct WasiThread { state: Arc, layout: WasiMemoryLayout, + start: ThreadStartType, // This is used for stack rewinds rewind: Option, @@ -116,6 +118,11 @@ impl WasiThread { self.rewind.take() } + /// Gets the thread start type for this thread + pub fn thread_start_type(&self) -> ThreadStartType { + self.start.clone() + } + /// Returns true if a rewind of a particular type has been queued /// for processed by a rewind operation pub(crate) fn has_rewind_of_type(&self, type_: HandleRewindType) -> bool { @@ -195,19 +202,7 @@ impl Drop for WasiThreadRunGuard { } /// Represents the memory layout of the parts that the thread itself uses -#[derive(Debug, Default, Clone)] -pub struct WasiMemoryLayout { - /// This is the top part of the stack (stacks go backwards) - pub stack_upper: u64, - /// This is the bottom part of the stack (anything more below this is a stack overflow) - pub stack_lower: u64, - /// Piece of memory that is marked as none readable/writable so stack overflows cause an exception - /// TODO: This field will need to be used to mark the guard memory as inaccessible - #[allow(dead_code)] - pub guard_size: u64, - /// Total size of the stack - pub stack_size: u64, -} +pub use wasmer_wasix_types::wasix::WasiMemoryLayout; // Contains the result of a rewind operation #[derive(Clone, Debug)] @@ -246,6 +241,7 @@ impl WasiThread { status: Arc, guard: TaskCountGuard, layout: WasiMemoryLayout, + start: ThreadStartType, ) -> Self { Self { state: Arc::new(WasiThreadState { @@ -261,6 +257,7 @@ impl WasiThread { _task_count_guard: guard, }), layout, + start, rewind: None, } } diff --git a/lib/wasix/src/rewind.rs b/lib/wasix/src/rewind.rs index 92f72a6edc3..d7f1d8dc8e4 100644 --- a/lib/wasix/src/rewind.rs +++ b/lib/wasix/src/rewind.rs @@ -2,7 +2,10 @@ use std::pin::Pin; use bytes::Bytes; use futures::Future; -use wasmer_wasix_types::wasi::Errno; +use wasmer_wasix_types::{ + wasi::Errno, + wasix::{ThreadStartType, WasiMemoryLayout}, +}; /// Future that will be polled by asyncify methods #[doc(hidden)] @@ -24,6 +27,10 @@ pub struct RewindState { pub rewind_stack: Bytes, /// All the global data stored in the store pub store_data: Bytes, + /// Describes the type of thread start + pub start: ThreadStartType, + /// Layout of the memory, + pub layout: WasiMemoryLayout, /// Flag that indicates if this rewind is 64-bit or 32-bit memory based pub is_64bit: bool, } diff --git a/lib/wasix/src/state/env.rs b/lib/wasix/src/state/env.rs index 3ba1e0d4d10..70ae7884c5b 100644 --- a/lib/wasix/src/state/env.rs +++ b/lib/wasix/src/state/env.rs @@ -20,6 +20,7 @@ use wasmer::{ use wasmer_wasix_types::{ types::Signal, wasi::{Errno, ExitCode, Snapshot0Clockid}, + wasix::ThreadStartType, }; #[cfg(feature = "journal")] @@ -373,7 +374,7 @@ impl WasiEnv { /// Forking the WasiState is used when either fork or vfork is called pub fn fork(&self) -> Result<(Self, WasiThreadHandle), ControlPlaneError> { let process = self.control_plane.new_process(self.process.module_hash)?; - let handle = process.new_thread(self.layout.clone())?; + let handle = process.new_thread(self.layout.clone(), ThreadStartType::MainThread)?; let thread = handle.as_thread(); thread.copy_stack_from(&self.thread); @@ -455,6 +456,7 @@ impl WasiEnv { self.process.finished.clone(), self.process.compute.must_upgrade().register_task()?, self.thread.memory_layout().clone(), + self.thread.thread_start_type(), ); Ok(()) @@ -497,7 +499,7 @@ impl WasiEnv { let thread = if let Some(t) = init.thread { t } else { - process.new_thread(layout.clone())? + process.new_thread(layout.clone(), ThreadStartType::MainThread)? }; let mut env = Self { diff --git a/lib/wasix/src/syscalls/journal.rs b/lib/wasix/src/syscalls/journal.rs index 2b71591880a..ee4e620ae5a 100644 --- a/lib/wasix/src/syscalls/journal.rs +++ b/lib/wasix/src/syscalls/journal.rs @@ -289,6 +289,8 @@ pub unsafe fn restore_snapshot( memory_stack, store_data, is_64bit, + start, + layout, } => { if cur_module_hash != journal_module_hash { continue; @@ -298,6 +300,8 @@ pub unsafe fn restore_snapshot( memory_stack: memory_stack.to_vec().into(), rewind_stack: call_stack.to_vec().into(), store_data: store_data.to_vec().into(), + start, + layout, is_64bit, }; @@ -772,14 +776,38 @@ pub unsafe fn restore_snapshot( JournalEffector::apply_tty_set(&mut ctx, state).map_err(anyhow_err_to_runtime_err)?; } - // We do not yet support multi threading - if !spawn_threads.is_empty() { - return Err(WasiRuntimeError::Runtime(RuntimeError::user( - anyhow::format_err!( - "Snapshot restoration does not currently support multiple threads." + // If the main thread is not being restored then don't bother + // attempting to restore the spawned threads either as the + // main process is effectively in an exited state + if rewind.is_none() { + spawn_threads.clear(); + } + + // Spawn all the threads + for (thread_id, thread_state) in spawn_threads { + if thread_state.is_64bit { + JournalEffector::apply_thread_state::( + &mut ctx, + thread_id, + thread_state.memory_stack, + thread_state.rewind_stack, + thread_state.store_data, + thread_state.start, + thread_state.layout, ) - .into(), - ))); + .map_err(anyhow_err_to_runtime_err)?; + } else { + JournalEffector::apply_thread_state::( + &mut ctx, + thread_id, + thread_state.memory_stack, + thread_state.rewind_stack, + thread_state.store_data, + thread_state.start, + thread_state.layout, + ) + .map_err(anyhow_err_to_runtime_err)?; + } } Ok(rewind) diff --git a/lib/wasix/src/syscalls/mod.rs b/lib/wasix/src/syscalls/mod.rs index 347c022b2ef..f7608a8045d 100644 --- a/lib/wasix/src/syscalls/mod.rs +++ b/lib/wasix/src/syscalls/mod.rs @@ -29,6 +29,7 @@ use tracing::instrument; pub use wasi::*; pub use wasix::*; use wasmer_journal::SnapshotTrigger; +use wasmer_wasix_types::wasix::ThreadStartType; pub mod legacy; @@ -984,12 +985,14 @@ pub(crate) fn deep_sleep( .serialize() .unwrap(); let store_data = Bytes::from(store_data); + let thread_start = ctx.data().thread.thread_start_type(); // Perform the unwind action let tasks = ctx.data().tasks().clone(); let res = unwind::(ctx, move |mut ctx, memory_stack, rewind_stack| { let memory_stack = memory_stack.freeze(); let rewind_stack = rewind_stack.freeze(); + let thread_layout = ctx.data().thread.memory_layout().clone(); // If journal'ing is enabled then we dump the stack into the journal if ctx.data().enable_journal { @@ -1008,12 +1011,15 @@ pub(crate) fn deep_sleep( // Write our thread state to the snapshot let tid = ctx.data().thread.tid(); + let thread_start = ctx.data().thread.thread_start_type(); if let Err(err) = JournalEffector::save_thread_state::( &mut ctx, tid, memory_stack.clone(), rewind_stack.clone(), store_data.clone(), + thread_start, + thread_layout.clone(), ) { return wasmer_types::OnCalledAction::Trap(err.into()); } @@ -1046,6 +1052,8 @@ pub(crate) fn deep_sleep( memory_stack, rewind_stack, store_data, + start: thread_start, + layout: thread_layout, is_64bit: M::is_64bit(), }, }))) diff --git a/lib/wasix/src/syscalls/wasix/thread_spawn.rs b/lib/wasix/src/syscalls/wasix/thread_spawn.rs index ead6a31865e..8ff2fc9f532 100644 --- a/lib/wasix/src/syscalls/wasix/thread_spawn.rs +++ b/lib/wasix/src/syscalls/wasix/thread_spawn.rs @@ -75,7 +75,10 @@ pub(crate) fn thread_spawn_internal( tracing::trace!("spawn with layout {:?}", layout); // Create the handle that represents this thread - let mut thread_handle = match env.process.new_thread(layout.clone()) { + let thread_start = ThreadStartType::ThreadSpawn { + start_ptr: start_ptr_offset.into(), + }; + let mut thread_handle = match env.process.new_thread(layout.clone(), thread_start) { Ok(h) => Arc::new(h), Err(err) => { error!( From 3eeb86f9c60df4822896ab4e5d9a7e1957e7f7c1 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 8 Feb 2024 20:51:39 +1100 Subject: [PATCH 36/67] Added the functionality that spawns threads --- .../src/journal/effector/thread_state.rs | 9 ++++++-- lib/wasix/src/os/task/process.rs | 20 ++++++++++++----- lib/wasix/src/syscalls/wasi/thread_spawn.rs | 2 +- lib/wasix/src/syscalls/wasix/thread_spawn.rs | 22 ++++++++++++++----- 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/lib/wasix/src/journal/effector/thread_state.rs b/lib/wasix/src/journal/effector/thread_state.rs index 3f7b819b87d..f962b32b2fd 100644 --- a/lib/wasix/src/journal/effector/thread_state.rs +++ b/lib/wasix/src/journal/effector/thread_state.rs @@ -1,6 +1,6 @@ use wasmer_wasix_types::wasix::ThreadStartType; -use crate::os::task::thread::WasiMemoryLayout; +use crate::{os::task::thread::WasiMemoryLayout, syscalls::thread_spawn_internal_phase2}; use super::*; @@ -49,7 +49,12 @@ impl JournalEffector { }; // Create the thread for this ID - //ctx.data().process.new_thread(layout, start); + let thread_handle = ctx.data().process.new_thread_with_id(layout, start, tid)?; + + // Now spawn the thread itself + thread_spawn_internal_phase2(&mut ctx, thread_handle, layout, start_ptr) + .map_err(|err| anyhow::format_err!("failed to spawn thread"))?; + Ok(()) } } diff --git a/lib/wasix/src/os/task/process.rs b/lib/wasix/src/os/task/process.rs index 0a870a12f85..2ac09aaae69 100644 --- a/lib/wasix/src/os/task/process.rs +++ b/lib/wasix/src/os/task/process.rs @@ -384,10 +384,7 @@ impl WasiProcess { let task_count_guard = control_plane.register_task()?; // Determine if its the main thread or not - let is_main = { - let inner = self.inner.0.lock().unwrap(); - inner.thread_count == 0 - }; + let is_main = matches!(start, ThreadStartType::MainThread); // Generate a new process ID (this is because the process ID and thread ID // address space must not overlap in libc). For the main proecess the TID=PID @@ -398,6 +395,19 @@ impl WasiProcess { tid.into() }; + self.new_thread_with_id(layout, start, tid) + } + + /// Creates a a thread and returns it + pub fn new_thread_with_id( + &self, + layout: WasiMemoryLayout, + start: ThreadStartType, + tid: WasiThreadId, + ) -> Result { + let control_plane = self.compute.must_upgrade(); + let task_count_guard = control_plane.register_task()?; + // The wait finished should be the process version if its the main thread let mut inner = self.inner.0.lock().unwrap(); let finished = if is_main { @@ -410,7 +420,7 @@ impl WasiProcess { let ctrl = WasiThread::new( self.pid(), tid, - is_main, + matches!(start, ThreadStartType::MainThread), finished, task_count_guard, layout, diff --git a/lib/wasix/src/syscalls/wasi/thread_spawn.rs b/lib/wasix/src/syscalls/wasi/thread_spawn.rs index 9009aea6010..aab3dc64f13 100644 --- a/lib/wasix/src/syscalls/wasi/thread_spawn.rs +++ b/lib/wasix/src/syscalls/wasi/thread_spawn.rs @@ -21,7 +21,7 @@ pub fn thread_spawn( mut ctx: FunctionEnvMut<'_, WasiEnv>, start_ptr: WasmPtr, M>, ) -> i32 { - thread_spawn_internal(&mut ctx, start_ptr) + thread_spawn_internal_phase1(&mut ctx, start_ptr) .map(|tid| tid as i32) .map_err(|errno| errno as i32) .unwrap_or_else(|err| -err) diff --git a/lib/wasix/src/syscalls/wasix/thread_spawn.rs b/lib/wasix/src/syscalls/wasix/thread_spawn.rs index 8ff2fc9f532..c5026c39f8f 100644 --- a/lib/wasix/src/syscalls/wasix/thread_spawn.rs +++ b/lib/wasix/src/syscalls/wasix/thread_spawn.rs @@ -34,7 +34,7 @@ pub fn thread_spawn_v2( ret_tid: WasmPtr, ) -> Errno { // Create the thread - let tid = wasi_try!(thread_spawn_internal(&mut ctx, start_ptr)); + let tid = wasi_try!(thread_spawn_internal_phase1(&mut ctx, start_ptr)); // Success let memory = unsafe { ctx.data().memory_view(&ctx) }; @@ -42,7 +42,7 @@ pub fn thread_spawn_v2( Errno::Success } -pub(crate) fn thread_spawn_internal( +pub fn thread_spawn_internal_phase1( ctx: &mut FunctionEnvMut<'_, WasiEnv>, start_ptr: WasmPtr, M>, ) -> Result { @@ -53,9 +53,6 @@ pub(crate) fn thread_spawn_internal( let tasks = env.tasks().clone(); let start_ptr_offset = start_ptr.offset(); - // We extract the memory which will be passed to the thread - let thread_memory = unsafe { env.inner() }.memory_clone(); - // Read the properties about the stack which we will use for asyncify let layout = { let start: ThreadStart = start_ptr.read(&memory).map_err(mem_error_to_wasi)?; @@ -92,6 +89,19 @@ pub(crate) fn thread_spawn_internal( let thread_id: Tid = thread_handle.id().into(); Span::current().record("tid", thread_id); + thread_spawn_internal_phase2(ctx, thread_handle) +} + +pub fn thread_spawn_internal_phase2( + ctx: &mut FunctionEnvMut<'_, WasiEnv>, + thread_handle: Arc, + layout: WasiMemoryLayout, + start_ptr_offset: M::Offset, +) -> Result { + // We extract the memory which will be passed to the thread + let env = ctx.data(); + let thread_memory = unsafe { env.inner() }.memory_clone(); + // We capture some local variables let state = env.state.clone(); let mut thread_env = env.clone(); @@ -99,7 +109,7 @@ pub(crate) fn thread_spawn_internal( thread_env.layout = layout; // TODO: Currently asynchronous threading does not work with multi - // threading but it does work for the main thread. This will + // threading in JS but it does work for the main thread. This will // require more work to find out why. thread_env.enable_deep_sleep = if cfg!(feature = "js") { false From c187126a3e0be43a44f778aedad22eded76a3b3d Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 8 Feb 2024 21:01:21 +1100 Subject: [PATCH 37/67] Now spawning the sub threads correctly on journal resume --- .../src/journal/effector/thread_state.rs | 34 ++++++++++++++++--- lib/wasix/src/os/task/process.rs | 5 +-- lib/wasix/src/syscalls/wasix/thread_spawn.rs | 12 +++++-- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/lib/wasix/src/journal/effector/thread_state.rs b/lib/wasix/src/journal/effector/thread_state.rs index f962b32b2fd..14771ccef80 100644 --- a/lib/wasix/src/journal/effector/thread_state.rs +++ b/lib/wasix/src/journal/effector/thread_state.rs @@ -1,6 +1,10 @@ +use std::sync::Arc; + use wasmer_wasix_types::wasix::ThreadStartType; -use crate::{os::task::thread::WasiMemoryLayout, syscalls::thread_spawn_internal_phase2}; +use crate::{ + os::task::thread::WasiMemoryLayout, syscalls::thread_spawn_internal_phase2, RewindState, +}; use super::*; @@ -30,7 +34,7 @@ impl JournalEffector { pub fn apply_thread_state( ctx: &mut FunctionEnvMut<'_, WasiEnv>, - id: WasiThreadId, + tid: WasiThreadId, memory_stack: Bytes, rewind_stack: Bytes, store_data: Bytes, @@ -49,11 +53,31 @@ impl JournalEffector { }; // Create the thread for this ID - let thread_handle = ctx.data().process.new_thread_with_id(layout, start, tid)?; + let thread_handle = Arc::new(ctx.data().process.new_thread_with_id( + layout.clone(), + start, + tid, + )?); // Now spawn the thread itself - thread_spawn_internal_phase2(&mut ctx, thread_handle, layout, start_ptr) - .map_err(|err| anyhow::format_err!("failed to spawn thread"))?; + thread_spawn_internal_phase2::( + ctx, + thread_handle, + layout.clone(), + start_ptr, + Some(( + RewindState { + memory_stack, + rewind_stack, + store_data, + start, + layout, + is_64bit: M::is_64bit(), + }, + Bytes::new(), + )), + ) + .map_err(|err| anyhow::format_err!("failed to spawn thread - {}", err))?; Ok(()) } diff --git a/lib/wasix/src/os/task/process.rs b/lib/wasix/src/os/task/process.rs index 2ac09aaae69..12a0b623b3d 100644 --- a/lib/wasix/src/os/task/process.rs +++ b/lib/wasix/src/os/task/process.rs @@ -381,7 +381,6 @@ impl WasiProcess { start: ThreadStartType, ) -> Result { let control_plane = self.compute.must_upgrade(); - let task_count_guard = control_plane.register_task()?; // Determine if its the main thread or not let is_main = matches!(start, ThreadStartType::MainThread); @@ -408,6 +407,8 @@ impl WasiProcess { let control_plane = self.compute.must_upgrade(); let task_count_guard = control_plane.register_task()?; + let is_main = matches!(start, ThreadStartType::MainThread); + // The wait finished should be the process version if its the main thread let mut inner = self.inner.0.lock().unwrap(); let finished = if is_main { @@ -420,7 +421,7 @@ impl WasiProcess { let ctrl = WasiThread::new( self.pid(), tid, - matches!(start, ThreadStartType::MainThread), + is_main, finished, task_count_guard, layout, diff --git a/lib/wasix/src/syscalls/wasix/thread_spawn.rs b/lib/wasix/src/syscalls/wasix/thread_spawn.rs index c5026c39f8f..6dfac3d2d89 100644 --- a/lib/wasix/src/syscalls/wasix/thread_spawn.rs +++ b/lib/wasix/src/syscalls/wasix/thread_spawn.rs @@ -89,7 +89,11 @@ pub fn thread_spawn_internal_phase1( let thread_id: Tid = thread_handle.id().into(); Span::current().record("tid", thread_id); - thread_spawn_internal_phase2(ctx, thread_handle) + // Spawn the thread + thread_spawn_internal_phase2::(ctx, thread_handle, layout, start_ptr_offset, None)?; + + // Success + Ok(thread_id) } pub fn thread_spawn_internal_phase2( @@ -97,9 +101,11 @@ pub fn thread_spawn_internal_phase2( thread_handle: Arc, layout: WasiMemoryLayout, start_ptr_offset: M::Offset, -) -> Result { + rewind_state: Option<(RewindState, Bytes)>, +) -> Result<(), Errno> { // We extract the memory which will be passed to the thread let env = ctx.data(); + let tasks = env.tasks().clone(); let thread_memory = unsafe { env.inner() }.memory_clone(); // We capture some local variables @@ -152,7 +158,7 @@ pub fn thread_spawn_internal_phase2( .map_err(Into::::into)?; // Success - Ok(thread_id) + Ok(()) } /// Calls the module From da218641b3a6e317a4a728d121718122fef9dc21 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 8 Feb 2024 21:13:51 +1100 Subject: [PATCH 38/67] Fixed an issue where the main thread was not rewinding properly --- lib/wasix/src/syscalls/journal.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/wasix/src/syscalls/journal.rs b/lib/wasix/src/syscalls/journal.rs index ee4e620ae5a..3a5f416f2ba 100644 --- a/lib/wasix/src/syscalls/journal.rs +++ b/lib/wasix/src/syscalls/journal.rs @@ -370,7 +370,9 @@ pub unsafe fn restore_snapshot( continue; } - rewind = staging_rewind.take(); + if let Some(new_rewind) = staging_rewind.take() { + rewind.replace(new_rewind); + } for thread_id in staging_kill_thread.drain() { spawn_threads.remove(&thread_id); } From f2dfd5c10b5b0d6de5e6eed6a06a183ba634fffe Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 8 Feb 2024 21:21:23 +1100 Subject: [PATCH 39/67] Fixed an issue where the threads were not properly rewinding on journals --- lib/wasix/src/syscalls/mod.rs | 11 ++++++++--- lib/wasix/src/syscalls/wasix/thread_spawn.rs | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/wasix/src/syscalls/mod.rs b/lib/wasix/src/syscalls/mod.rs index f7608a8045d..cc437f4b42a 100644 --- a/lib/wasix/src/syscalls/mod.rs +++ b/lib/wasix/src/syscalls/mod.rs @@ -1402,9 +1402,14 @@ where set_memory_stack::(env, &mut store, memory_stack); if let Some(rewind_result) = result.rewind_result { - let ret = bincode::deserialize(&rewind_result) - .expect("failed to deserialize the rewind result"); - Some(Some(ret)) + match rewind_result.len() { + 0 => Some(None), + _ => { + let ret = bincode::deserialize(&rewind_result) + .expect("failed to deserialize the rewind result"); + Some(Some(ret)) + } + } } else { Some(None) } diff --git a/lib/wasix/src/syscalls/wasix/thread_spawn.rs b/lib/wasix/src/syscalls/wasix/thread_spawn.rs index 6dfac3d2d89..43f71401cf1 100644 --- a/lib/wasix/src/syscalls/wasix/thread_spawn.rs +++ b/lib/wasix/src/syscalls/wasix/thread_spawn.rs @@ -129,7 +129,7 @@ pub fn thread_spawn_internal_phase2( let thread_handle = thread_handle; move |ctx: WasiFunctionEnv, mut store: Store| { // Call the thread - call_module::(ctx, store, start_ptr_offset, thread_handle, None) + call_module::(ctx, store, start_ptr_offset, thread_handle, rewind_state) } }; From 354e38cb7c210363ebfec285bc74cea8215cc814 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 8 Feb 2024 22:33:04 +1100 Subject: [PATCH 40/67] Failed WASM processes now properly terminate running connections --- Cargo.lock | 13 +++++ lib/wasix/Cargo.toml | 1 + .../runners/dproxy/hyper_proxy/connector.rs | 10 +++- .../src/runners/dproxy/hyper_proxy/stream.rs | 48 +++++++++++++++++-- .../src/runners/dproxy/socket_manager.rs | 4 ++ 5 files changed, 71 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 96f85e30918..ed7c9d91888 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4869,6 +4869,18 @@ dependencies = [ "serde_json", ] +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util 0.7.10", +] + [[package]] name = "tokio-tungstenite" version = "0.20.1" @@ -6537,6 +6549,7 @@ dependencies = [ "termios", "thiserror", "tokio", + "tokio-stream", "tower", "tower-http", "tracing", diff --git a/lib/wasix/Cargo.toml b/lib/wasix/Cargo.toml index 31c50636b3a..880943b33d9 100644 --- a/lib/wasix/Cargo.toml +++ b/lib/wasix/Cargo.toml @@ -47,6 +47,7 @@ tokio = { version = "1", features = [ "time", "rt", ], default_features = false } +tokio-stream = { version = "0.1", features = [ "sync" ] } futures = { version = "0.3" } # used by feature='os' async-trait = { version = "^0.1" } diff --git a/lib/wasix/src/runners/dproxy/hyper_proxy/connector.rs b/lib/wasix/src/runners/dproxy/hyper_proxy/connector.rs index 56adb560dd7..a309c8c18c8 100644 --- a/lib/wasix/src/runners/dproxy/hyper_proxy/connector.rs +++ b/lib/wasix/src/runners/dproxy/hyper_proxy/connector.rs @@ -1,5 +1,7 @@ use std::sync::Arc; +use tokio_stream::wrappers::BroadcastStream; + use super::socket_manager::SocketManager; use super::*; @@ -30,9 +32,15 @@ impl Service for HyperProxyConnector { fn call(&mut self, _dst: Uri) -> Self::Future { let this = self.clone(); Box::pin(async move { + let terminate_rx = this.socket_manager.terminate_rx(); let socket = this.socket_manager.acquire_http_socket().await?; let (tx, rx) = socket.split(); - Ok(HyperProxyStream { tx, rx }) + Ok(HyperProxyStream { + tx, + rx, + terminate: BroadcastStream::new(terminate_rx), + terminated: false, + }) }) } } diff --git a/lib/wasix/src/runners/dproxy/hyper_proxy/stream.rs b/lib/wasix/src/runners/dproxy/hyper_proxy/stream.rs index a04d240c864..fd5d0d17493 100644 --- a/lib/wasix/src/runners/dproxy/hyper_proxy/stream.rs +++ b/lib/wasix/src/runners/dproxy/hyper_proxy/stream.rs @@ -1,6 +1,8 @@ use std::io; +use futures::Stream; use hyper::client::connect::Connected; +use tokio_stream::wrappers::BroadcastStream; use virtual_net::tcp_pair::{TcpSocketHalfRx, TcpSocketHalfTx}; use super::*; @@ -9,6 +11,8 @@ use super::*; pub struct HyperProxyStream { pub(super) tx: TcpSocketHalfTx, pub(super) rx: TcpSocketHalfRx, + pub(super) terminate: BroadcastStream<()>, + pub(super) terminated: bool, } impl AsyncRead for HyperProxyStream { @@ -18,7 +22,16 @@ impl AsyncRead for HyperProxyStream { cx: &mut Context, buf: &mut ReadBuf<'_>, ) -> Poll> { - Pin::new(&mut self.rx).poll_read(cx, buf) + if let Poll::Ready(ret) = Pin::new(&mut self.rx).poll_read(cx, buf) { + return Poll::Ready(ret); + } + if self.terminated { + return Poll::Ready(Err(io::ErrorKind::ConnectionReset.into())); + } + if let Poll::Ready(Some(_)) = Pin::new(&mut self.terminate).poll_next(cx) { + return Poll::Ready(Err(io::ErrorKind::ConnectionReset.into())); + } + Poll::Pending } } @@ -29,12 +42,30 @@ impl AsyncWrite for HyperProxyStream { cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - Pin::new(&mut self.tx).poll_write(cx, buf) + if let Poll::Ready(ret) = Pin::new(&mut self.tx).poll_write(cx, buf) { + return Poll::Ready(ret); + } + if self.terminated { + return Poll::Ready(Err(io::ErrorKind::ConnectionReset.into())); + } + if let Poll::Ready(Some(_)) = Pin::new(&mut self.terminate).poll_next(cx) { + return Poll::Ready(Err(io::ErrorKind::ConnectionReset.into())); + } + Poll::Pending } #[inline] fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.tx).poll_flush(cx) + if let Poll::Ready(ret) = Pin::new(&mut self.tx).poll_flush(cx) { + return Poll::Ready(ret); + } + if self.terminated { + return Poll::Ready(Err(io::ErrorKind::ConnectionReset.into())); + } + if let Poll::Ready(Some(_)) = Pin::new(&mut self.terminate).poll_next(cx) { + return Poll::Ready(Err(io::ErrorKind::ConnectionReset.into())); + } + Poll::Pending } #[inline] @@ -42,7 +73,16 @@ impl AsyncWrite for HyperProxyStream { mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - Pin::new(&mut self.tx).poll_shutdown(cx) + if let Poll::Ready(ret) = Pin::new(&mut self.tx).poll_shutdown(cx) { + return Poll::Ready(ret); + } + if self.terminated { + return Poll::Ready(Err(io::ErrorKind::ConnectionReset.into())); + } + if let Poll::Ready(Some(_)) = Pin::new(&mut self.terminate).poll_next(cx) { + return Poll::Ready(Err(io::ErrorKind::ConnectionReset.into())); + } + Poll::Pending } } diff --git a/lib/wasix/src/runners/dproxy/socket_manager.rs b/lib/wasix/src/runners/dproxy/socket_manager.rs index 1a0c3ffc81a..9198a35e626 100644 --- a/lib/wasix/src/runners/dproxy/socket_manager.rs +++ b/lib/wasix/src/runners/dproxy/socket_manager.rs @@ -52,6 +52,10 @@ impl SocketManager { self.terminate_all.send(()).ok(); } + pub fn terminate_rx(&self) -> broadcast::Receiver<()> { + self.terminate_all.subscribe() + } + pub async fn acquire_http_socket(&self) -> anyhow::Result { let mut rx_terminate = self.terminate_all.subscribe(); From d7255c466997a2422f54bde6b7cb010849f7a87b Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 8 Feb 2024 23:06:57 +1100 Subject: [PATCH 41/67] Fixed the thread resuming however there is now a tokio runtime problem --- lib/wasix/src/bin_factory/exec.rs | 10 +++--- .../src/journal/effector/thread_state.rs | 6 ++-- lib/wasix/src/os/task/control_plane.rs | 22 +++++++++--- lib/wasix/src/os/task/process.rs | 11 ++++-- lib/wasix/src/os/task/thread.rs | 24 +++++++++++-- lib/wasix/src/rewind.rs | 4 ++- lib/wasix/src/state/func_env.rs | 3 +- lib/wasix/src/state/run.rs | 9 +++-- lib/wasix/src/syscalls/mod.rs | 36 +++++++++++-------- lib/wasix/src/syscalls/wasix/proc_fork.rs | 9 +++-- lib/wasix/src/syscalls/wasix/thread_spawn.rs | 8 ++--- 11 files changed, 100 insertions(+), 42 deletions(-) diff --git a/lib/wasix/src/bin_factory/exec.rs b/lib/wasix/src/bin_factory/exec.rs index dfc8d02ec9b..71614f875f7 100644 --- a/lib/wasix/src/bin_factory/exec.rs +++ b/lib/wasix/src/bin_factory/exec.rs @@ -1,14 +1,16 @@ use std::{pin::Pin, sync::Arc}; use crate::{ - os::task::{thread::WasiThreadRunGuard, TaskJoinHandle}, + os::task::{ + thread::{RewindResultType, WasiThreadRunGuard}, + TaskJoinHandle, + }, runtime::task_manager::{ TaskWasm, TaskWasmRecycle, TaskWasmRecycleProperties, TaskWasmRunProperties, }, syscalls::rewind_ext, RewindState, SpawnError, WasiError, WasiRuntimeError, }; -use bytes::Bytes; use futures::Future; use tracing::*; use wasmer::{Function, FunctionEnvMut, Memory32, Memory64, Module, Store}; @@ -182,7 +184,7 @@ fn call_module( ctx: WasiFunctionEnv, mut store: Store, handle: WasiThreadRunGuard, - rewind_state: Option<(RewindState, Option)>, + rewind_state: Option<(RewindState, RewindResultType)>, recycle: Option>, ) { let env = ctx.data(&store); @@ -249,7 +251,7 @@ fn call_module( ctx, store, handle, - Some((rewind, Some(rewind_result))), + Some((rewind, RewindResultType::RewindWithResult(rewind_result))), recycle, ); } diff --git a/lib/wasix/src/journal/effector/thread_state.rs b/lib/wasix/src/journal/effector/thread_state.rs index 14771ccef80..9e969ec2abb 100644 --- a/lib/wasix/src/journal/effector/thread_state.rs +++ b/lib/wasix/src/journal/effector/thread_state.rs @@ -3,7 +3,9 @@ use std::sync::Arc; use wasmer_wasix_types::wasix::ThreadStartType; use crate::{ - os::task::thread::WasiMemoryLayout, syscalls::thread_spawn_internal_phase2, RewindState, + os::task::thread::{RewindResultType, WasiMemoryLayout}, + syscalls::thread_spawn_internal_phase2, + RewindState, }; use super::*; @@ -74,7 +76,7 @@ impl JournalEffector { layout, is_64bit: M::is_64bit(), }, - Bytes::new(), + RewindResultType::RewindRestart, )), ) .map_err(|err| anyhow::format_err!("failed to spawn thread - {}", err))?; diff --git a/lib/wasix/src/os/task/control_plane.rs b/lib/wasix/src/os/task/control_plane.rs index 96d5575e62e..93fab4e23be 100644 --- a/lib/wasix/src/os/task/control_plane.rs +++ b/lib/wasix/src/os/task/control_plane.rs @@ -203,6 +203,8 @@ pub enum ControlPlaneError { #[cfg(test)] mod tests { + use wasmer_wasix_types::wasix::ThreadStartType; + use crate::os::task::thread::WasiMemoryLayout; use super::*; @@ -216,8 +218,12 @@ mod tests { }); let p1 = p.new_process(ModuleHash::random()).unwrap(); - let _t1 = p1.new_thread(WasiMemoryLayout::default()).unwrap(); - let _t2 = p1.new_thread(WasiMemoryLayout::default()).unwrap(); + let _t1 = p1 + .new_thread(WasiMemoryLayout::default(), ThreadStartType::MainThread) + .unwrap(); + let _t2 = p1 + .new_thread(WasiMemoryLayout::default(), ThreadStartType::MainThread) + .unwrap(); assert_eq!( p.new_process(ModuleHash::random()).unwrap_err(), @@ -236,11 +242,17 @@ mod tests { let p1 = p.new_process(ModuleHash::random()).unwrap(); for _ in 0..10 { - let _thread = p1.new_thread(WasiMemoryLayout::default()).unwrap(); + let _thread = p1 + .new_thread(WasiMemoryLayout::default(), ThreadStartType::MainThread) + .unwrap(); } - let _t1 = p1.new_thread(WasiMemoryLayout::default()).unwrap(); - let _t2 = p1.new_thread(WasiMemoryLayout::default()).unwrap(); + let _t1 = p1 + .new_thread(WasiMemoryLayout::default(), ThreadStartType::MainThread) + .unwrap(); + let _t2 = p1 + .new_thread(WasiMemoryLayout::default(), ThreadStartType::MainThread) + .unwrap(); assert_eq!( p.new_process(ModuleHash::random()).unwrap_err(), diff --git a/lib/wasix/src/os/task/process.rs b/lib/wasix/src/os/task/process.rs index 12a0b623b3d..d1ba420c8b8 100644 --- a/lib/wasix/src/os/task/process.rs +++ b/lib/wasix/src/os/task/process.rs @@ -205,7 +205,7 @@ impl WasiProcessInner { use wasmer::AsStoreMut; use wasmer_types::OnCalledAction; - use crate::{rewind_ext, WasiError}; + use crate::{os::task::thread::RewindResultType, rewind_ext, WasiError}; let guard = inner.0.lock().unwrap(); if guard.checkpoint == WasiProcessCheckpoint::Execute { // No checkpoint so just carry on @@ -286,8 +286,13 @@ impl WasiProcessInner { trace!("checkpoint finished"); // Rewind the stack and carry on - return match rewind_ext::(&mut ctx, memory_stack, rewind_stack, store_data, None) - { + return match rewind_ext::( + &mut ctx, + memory_stack, + rewind_stack, + store_data, + RewindResultType::RewindWithoutResult, + ) { Errno::Success => OnCalledAction::InvokeAgain, err => { tracing::warn!( diff --git a/lib/wasix/src/os/task/thread.rs b/lib/wasix/src/os/task/thread.rs index 1751dbb9675..6ff3a988ff7 100644 --- a/lib/wasix/src/os/task/thread.rs +++ b/lib/wasix/src/os/task/thread.rs @@ -128,11 +128,19 @@ impl WasiThread { pub(crate) fn has_rewind_of_type(&self, type_: HandleRewindType) -> bool { match type_ { HandleRewindType::ResultDriven => match &self.rewind { - Some(rewind) => rewind.rewind_result.is_some(), + Some(rewind) => match rewind.rewind_result { + RewindResultType::RewindRestart => true, + RewindResultType::RewindWithoutResult => false, + RewindResultType::RewindWithResult(_) => true, + }, None => false, }, HandleRewindType::ResultLess => match &self.rewind { - Some(rewind) => rewind.rewind_result.is_none(), + Some(rewind) => match rewind.rewind_result { + RewindResultType::RewindRestart => true, + RewindResultType::RewindWithoutResult => true, + RewindResultType::RewindWithResult(_) => false, + }, None => false, }, } @@ -204,6 +212,16 @@ impl Drop for WasiThreadRunGuard { /// Represents the memory layout of the parts that the thread itself uses pub use wasmer_wasix_types::wasix::WasiMemoryLayout; +#[derive(Clone, Debug)] +pub enum RewindResultType { + // The rewind must restart the operation it had already started + RewindRestart, + // The rewind has been triggered and should be handled but has not result + RewindWithoutResult, + // The rewind has been triggered and should be handled with the supplied result + RewindWithResult(Bytes), +} + // Contains the result of a rewind operation #[derive(Clone, Debug)] pub(crate) struct RewindResult { @@ -211,7 +229,7 @@ pub(crate) struct RewindResult { pub memory_stack: Bytes, /// Generic serialized object passed back to the rewind resumption code /// (uses the bincode serializer) - pub rewind_result: Option, + pub rewind_result: RewindResultType, } #[derive(Debug)] diff --git a/lib/wasix/src/rewind.rs b/lib/wasix/src/rewind.rs index d7f1d8dc8e4..fb3810b517a 100644 --- a/lib/wasix/src/rewind.rs +++ b/lib/wasix/src/rewind.rs @@ -7,6 +7,8 @@ use wasmer_wasix_types::{ wasix::{ThreadStartType, WasiMemoryLayout}, }; +use crate::os::task::thread::RewindResultType; + /// Future that will be polled by asyncify methods #[doc(hidden)] pub type AsyncifyFuture = dyn Future + Send + Sync + 'static; @@ -35,7 +37,7 @@ pub struct RewindState { pub is_64bit: bool, } -pub type RewindStateOption = Option<(RewindState, Option)>; +pub type RewindStateOption = Option<(RewindState, RewindResultType)>; /// Represents the work that will be done when a thread goes to deep sleep and /// includes the things needed to restore it again diff --git a/lib/wasix/src/state/func_env.rs b/lib/wasix/src/state/func_env.rs index 8cef5260ffb..4c28df65629 100644 --- a/lib/wasix/src/state/func_env.rs +++ b/lib/wasix/src/state/func_env.rs @@ -10,6 +10,7 @@ use wasmer_wasix_types::wasi::ExitCode; use crate::syscalls::restore_snapshot; use crate::{ import_object_for_all_wasi_versions, + os::task::thread::RewindResultType, runtime::SpawnMemoryType, state::WasiInstanceHandles, utils::{get_wasi_version, get_wasi_versions, store::restore_store_snapshot}, @@ -268,7 +269,7 @@ impl WasiFunctionEnv { return Err(err); } }; - rewind_state = rewind.map(|rewind| (rewind, None)); + rewind_state = rewind.map(|rewind| (rewind, RewindResultType::RewindRestart)); } self.data_mut(&mut store).replaying_journal = false; diff --git a/lib/wasix/src/state/run.rs b/lib/wasix/src/state/run.rs index 8f733ed4427..575883bea6e 100644 --- a/lib/wasix/src/state/run.rs +++ b/lib/wasix/src/state/run.rs @@ -2,7 +2,7 @@ use virtual_mio::InlineWaker; use wasmer::{RuntimeError, Store}; use wasmer_wasix_types::wasi::ExitCode; -use crate::{RewindStateOption, WasiError, WasiRuntimeError}; +use crate::{os::task::thread::RewindResultType, RewindStateOption, WasiError, WasiRuntimeError}; use super::*; @@ -158,7 +158,12 @@ fn handle_result( let tasks = env.data(&store).tasks().clone(); let rewind = work.rewind; let respawn = move |ctx, store, res| { - run_with_deep_sleep(store, Some((rewind, Some(res))), ctx, sender) + run_with_deep_sleep( + store, + Some((rewind, RewindResultType::RewindWithResult(res))), + ctx, + sender, + ) }; // Spawns the WASM process after a trigger diff --git a/lib/wasix/src/syscalls/mod.rs b/lib/wasix/src/syscalls/mod.rs index cc437f4b42a..492900bb5a2 100644 --- a/lib/wasix/src/syscalls/mod.rs +++ b/lib/wasix/src/syscalls/mod.rs @@ -124,7 +124,7 @@ use crate::{ journal::{DynJournal, JournalEffector}, os::task::{ process::{MaybeCheckpointResult, WasiProcessCheckpoint}, - thread::RewindResult, + thread::{RewindResult, RewindResultType}, }, runtime::task_manager::InlineWaker, utils::store::StoreSnapshot, @@ -1215,7 +1215,7 @@ where memory_stack, rewind_stack, store_data, - Some(rewind_result), + RewindResultType::RewindWithResult(rewind_result), ) } @@ -1226,7 +1226,7 @@ pub fn rewind_ext( memory_stack: Bytes, rewind_stack: Bytes, store_data: Bytes, - rewind_result: Option, + rewind_result: RewindResultType, ) -> Errno { // Store the memory stack so that it can be restored later ctx.data_mut().thread.set_rewind(RewindResult { @@ -1379,11 +1379,14 @@ pub(crate) unsafe fn handle_rewind_ext( where T: serde::de::DeserializeOwned, { - if !ctx.data().thread.has_rewind_of_type(type_) { + let env = ctx.data(); + if !env.thread.has_rewind_of_type(type_) { return None; }; // If the stack has been restored + let tid = env.tid(); + let pid = env.pid(); if let Some(result) = ctx.data_mut().thread.take_rewind() { // Deserialize the result let memory_stack = result.memory_stack; @@ -1401,19 +1404,24 @@ where let (env, mut store) = ctx.data_and_store_mut(); set_memory_stack::(env, &mut store, memory_stack); - if let Some(rewind_result) = result.rewind_result { - match rewind_result.len() { - 0 => Some(None), - _ => { - let ret = bincode::deserialize(&rewind_result) - .expect("failed to deserialize the rewind result"); - Some(Some(ret)) - } + match result.rewind_result { + RewindResultType::RewindRestart => { + debug!(%pid, %tid, "rewind for syscall restart"); + None + } + RewindResultType::RewindWithoutResult => { + debug!(%pid, %tid, "rewind with no result"); + Some(None) + } + RewindResultType::RewindWithResult(rewind_result) => { + debug!(%pid, %tid, "rewind with result (data={})", rewind_result.len()); + let ret = bincode::deserialize(&rewind_result) + .expect("failed to deserialize the rewind result"); + Some(Some(ret)) } - } else { - Some(None) } } else { + debug!(%pid, %tid, "rewind miss"); Some(None) } } diff --git a/lib/wasix/src/syscalls/wasix/proc_fork.rs b/lib/wasix/src/syscalls/wasix/proc_fork.rs index 8a8b627711c..1acaad14e57 100644 --- a/lib/wasix/src/syscalls/wasix/proc_fork.rs +++ b/lib/wasix/src/syscalls/wasix/proc_fork.rs @@ -237,7 +237,7 @@ fn run( ctx: WasiFunctionEnv, mut store: Store, child_handle: WasiThreadHandle, - rewind_state: Option<(RewindState, Bytes)>, + rewind_state: Option<(RewindState, RewindResultType)>, ) -> ExitCode { let env = ctx.data(&store); let tasks = env.tasks().clone(); @@ -252,7 +252,7 @@ fn run( rewind_state.memory_stack, rewind_state.rewind_stack, rewind_state.store_data, - Some(rewind_result), + rewind_result, ); if res != Errno::Success { return res.into(); @@ -289,7 +289,10 @@ fn run( ctx, store, child_handle, - Some((rewind_state, rewind_result)), + Some(( + rewind_state, + RewindResultType::RewindWithResult(rewind_result), + )), ); } }; diff --git a/lib/wasix/src/syscalls/wasix/thread_spawn.rs b/lib/wasix/src/syscalls/wasix/thread_spawn.rs index 43f71401cf1..01dc24b71b6 100644 --- a/lib/wasix/src/syscalls/wasix/thread_spawn.rs +++ b/lib/wasix/src/syscalls/wasix/thread_spawn.rs @@ -101,7 +101,7 @@ pub fn thread_spawn_internal_phase2( thread_handle: Arc, layout: WasiMemoryLayout, start_ptr_offset: M::Offset, - rewind_state: Option<(RewindState, Bytes)>, + rewind_state: Option<(RewindState, RewindResultType)>, ) -> Result<(), Errno> { // We extract the memory which will be passed to the thread let env = ctx.data(); @@ -167,7 +167,7 @@ fn call_module( mut store: Store, start_ptr_offset: M::Offset, thread_handle: Arc, - rewind_state: Option<(RewindState, Bytes)>, + rewind_state: Option<(RewindState, RewindResultType)>, ) -> Result { let env = ctx.data(&store); let tasks = env.tasks().clone(); @@ -230,7 +230,7 @@ fn call_module( rewind_state.memory_stack, rewind_state.rewind_stack, rewind_state.store_data, - Some(rewind_result), + rewind_result, ); if res != Errno::Success { return Err(res); @@ -259,7 +259,7 @@ fn call_module( store, start_ptr_offset, thread_handle, - Some((rewind, trigger_res)), + Some((rewind, RewindResultType::RewindWithResult(trigger_res))), ); } }; From 1fcf56b9b384c5c7edbd99f99db0e62a86150f4e Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Wed, 21 Feb 2024 09:53:37 +1100 Subject: [PATCH 42/67] Journal now properly cleans up when a snapshot is missing --- lib/wasix/src/syscalls/journal.rs | 53 ++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/lib/wasix/src/syscalls/journal.rs b/lib/wasix/src/syscalls/journal.rs index ed11205ca40..3b1d389b242 100644 --- a/lib/wasix/src/syscalls/journal.rs +++ b/lib/wasix/src/syscalls/journal.rs @@ -172,6 +172,7 @@ pub unsafe fn restore_snapshot( let mut update_memory: BTreeMap> = Default::default(); let mut staging_update_memory: BTreeMap> = Default::default(); + let mut ethereal_fds = HashSet::new(); let mut update_tty = None; // We capture the stdout and stderr while we replay @@ -208,6 +209,10 @@ pub unsafe fn restore_snapshot( stderr_fds.clear(); stdout_fds.insert(1 as WasiFd); stderr_fds.insert(2 as WasiFd); + for fd in ethereal_fds.drain() { + JournalEffector::apply_fd_close(&mut ctx, fd) + .map_err(anyhow_err_to_runtime_err)?; + } } else { JournalEffector::apply_process_exit(&mut ctx, exit_code) .map_err(anyhow_err_to_runtime_err)?; @@ -267,6 +272,10 @@ pub unsafe fn restore_snapshot( stderr_fds.clear(); stdout_fds.insert(1 as WasiFd); stderr_fds.insert(2 as WasiFd); + for fd in ethereal_fds.drain() { + JournalEffector::apply_fd_close(&mut ctx, fd) + .map_err(anyhow_err_to_runtime_err)?; + } } else { JournalEffector::apply_process_exit(&mut ctx, exit_code) .map_err(anyhow_err_to_runtime_err)?; @@ -321,6 +330,7 @@ pub unsafe fn restore_snapshot( } } crate::journal::JournalEntry::CloseFileDescriptorV1 { fd } => { + ethereal_fds.remove(&fd); stdout_fds.remove(&fd); stderr_fds.remove(&fd); JournalEffector::apply_fd_close(&mut ctx, fd).map_err(anyhow_err_to_runtime_err)?; @@ -400,6 +410,9 @@ pub unsafe fn restore_snapshot( if stderr_fds.remove(&old_fd) { stderr_fds.insert(new_fd); } + if ethereal_fds.remove(&old_fd) { + ethereal_fds.insert(new_fd); + } JournalEffector::apply_fd_renumber(&mut ctx, old_fd, new_fd) .map_err(anyhow_err_to_runtime_err)?; } @@ -417,6 +430,9 @@ pub unsafe fn restore_snapshot( if stderr_fds.contains(&original_fd) { stderr_fds.insert(copied_fd); } + if ethereal_fds.contains(&original_fd) { + ethereal_fds.insert(copied_fd); + } JournalEffector::apply_fd_duplicate(&mut ctx, original_fd, copied_fd) .map_err(anyhow_err_to_runtime_err)?; } @@ -504,10 +520,13 @@ pub unsafe fn restore_snapshot( JournalEffector::apply_chdir(&mut ctx, &path).map_err(anyhow_err_to_runtime_err)?; } crate::journal::JournalEntry::CreatePipeV1 { fd1, fd2 } => { + ethereal_fds.insert(fd1); + ethereal_fds.insert(fd2); JournalEffector::apply_fd_pipe(&mut ctx, fd1, fd2) .map_err(anyhow_err_to_runtime_err)?; } crate::journal::JournalEntry::EpollCreateV1 { fd } => { + ethereal_fds.insert(fd); JournalEffector::apply_epoll_create(&mut ctx, fd) .map_err(anyhow_err_to_runtime_err)?; } @@ -592,6 +611,7 @@ pub unsafe fn restore_snapshot( .map_err(anyhow_err_to_runtime_err)? } crate::journal::JournalEntry::SocketOpenV1 { af, ty, pt, fd } => { + ethereal_fds.insert(fd); JournalEffector::apply_sock_open(&mut ctx, af, ty, pt, fd) .map_err(anyhow_err_to_runtime_err)? } @@ -616,16 +636,19 @@ pub unsafe fn restore_snapshot( peer_addr, fd_flags, non_blocking: nonblocking, - } => JournalEffector::apply_sock_accepted( - &mut ctx, - listen_fd, - fd, - addr, - peer_addr, - fd_flags, - nonblocking, - ) - .map_err(anyhow_err_to_runtime_err)?, + } => { + ethereal_fds.insert(fd); + JournalEffector::apply_sock_accepted( + &mut ctx, + listen_fd, + fd, + addr, + peer_addr, + fd_flags, + nonblocking, + ) + .map_err(anyhow_err_to_runtime_err)? + } crate::journal::JournalEntry::SocketJoinIpv4MulticastV1 { fd, multiaddr, @@ -700,8 +723,11 @@ pub unsafe fn restore_snapshot( initial_val, flags, fd, - } => JournalEffector::apply_fd_event(&mut ctx, initial_val, flags, fd) - .map_err(anyhow_err_to_runtime_err)?, + } => { + ethereal_fds.insert(fd); + JournalEffector::apply_fd_event(&mut ctx, initial_val, flags, fd) + .map_err(anyhow_err_to_runtime_err)? + } } } @@ -742,6 +768,9 @@ pub unsafe fn restore_snapshot( stderr_fds.clear(); stdout_fds.insert(1 as WasiFd); stderr_fds.insert(2 as WasiFd); + for fd in ethereal_fds.drain() { + JournalEffector::apply_fd_close(&mut ctx, fd).map_err(anyhow_err_to_runtime_err)?; + } } else { JournalEffector::apply_process_exit(&mut ctx, None) .map_err(anyhow_err_to_runtime_err)?; From 621663479632608300d2207837d1c820b156c698 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Thu, 22 Feb 2024 18:06:33 +1100 Subject: [PATCH 43/67] Fixed the final pieces for a single threaded dproxy --- lib/journal/src/concrete/archived.rs | 17 + lib/journal/src/concrete/archived_from.rs | 3 + lib/journal/src/concrete/filter.rs | 1 + lib/journal/src/concrete/printing.rs | 3 + lib/journal/src/entry.rs | 3 + lib/wasix/src/net/socket.rs | 36 +- lib/wasix/src/runners/dproxy/factory.rs | 12 +- lib/wasix/src/state/func_env.rs | 15 +- lib/wasix/src/syscalls/journal.rs | 845 ------------------ .../syscalls/journal/actions/close_thread.rs | 33 + .../src/syscalls/journal/actions/fd_advise.rs | 16 + .../syscalls/journal/actions/fd_allocate.rs | 15 + .../src/syscalls/journal/actions/fd_close.rs | 11 + .../src/syscalls/journal/actions/fd_dup.rs | 25 + .../src/syscalls/journal/actions/fd_open.rs | 30 + .../syscalls/journal/actions/fd_renumber.rs | 25 + .../src/syscalls/journal/actions/fd_seek.rs | 15 + .../syscalls/journal/actions/fd_set_flags.rs | 14 + .../syscalls/journal/actions/fd_set_rights.rs | 20 + .../syscalls/journal/actions/fd_set_size.rs | 14 + .../syscalls/journal/actions/fd_set_times.rs | 16 + .../src/syscalls/journal/actions/fd_write.rs | 29 + .../syscalls/journal/actions/init_module.rs | 15 + lib/wasix/src/syscalls/journal/actions/mod.rs | 46 + .../journal/actions/path_set_times.rs | 26 + .../syscalls/journal/actions/process_exit.rs | 20 + .../syscalls/journal/actions/set_thread.rs | 51 ++ .../src/syscalls/journal/actions/snapshot.rs | 42 + .../src/syscalls/journal/actions/tty_set.rs | 26 + .../syscalls/journal/actions/update_memory.rs | 25 + .../src/syscalls/journal/clear_ethereal.rs | 18 + .../src/syscalls/journal/maybe_snapshot.rs | 30 + .../syscalls/journal/maybe_snapshot_many.rs | 44 + .../syscalls/journal/maybe_snapshot_once.rs | 44 + lib/wasix/src/syscalls/journal/mod.rs | 78 ++ lib/wasix/src/syscalls/journal/play_event.rs | 714 +++++++++++++++ .../src/syscalls/journal/restore_snapshot.rs | 89 ++ .../src/syscalls/journal/wait_for_snapshot.rs | 31 + 38 files changed, 1644 insertions(+), 853 deletions(-) delete mode 100644 lib/wasix/src/syscalls/journal.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/close_thread.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/fd_advise.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/fd_allocate.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/fd_close.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/fd_dup.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/fd_open.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/fd_renumber.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/fd_seek.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/fd_set_flags.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/fd_set_rights.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/fd_set_size.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/fd_set_times.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/fd_write.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/init_module.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/mod.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/path_set_times.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/process_exit.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/set_thread.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/snapshot.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/tty_set.rs create mode 100644 lib/wasix/src/syscalls/journal/actions/update_memory.rs create mode 100644 lib/wasix/src/syscalls/journal/clear_ethereal.rs create mode 100644 lib/wasix/src/syscalls/journal/maybe_snapshot.rs create mode 100644 lib/wasix/src/syscalls/journal/maybe_snapshot_many.rs create mode 100644 lib/wasix/src/syscalls/journal/maybe_snapshot_once.rs create mode 100644 lib/wasix/src/syscalls/journal/mod.rs create mode 100644 lib/wasix/src/syscalls/journal/play_event.rs create mode 100644 lib/wasix/src/syscalls/journal/restore_snapshot.rs create mode 100644 lib/wasix/src/syscalls/journal/wait_for_snapshot.rs diff --git a/lib/journal/src/concrete/archived.rs b/lib/journal/src/concrete/archived.rs index 32e7e260f41..6a800480389 100644 --- a/lib/journal/src/concrete/archived.rs +++ b/lib/journal/src/concrete/archived.rs @@ -83,6 +83,7 @@ pub enum JournalEntryRecordType { SocketSetOptTimeV1 = 57, SocketShutdownV1 = 58, SnapshotV1 = 59, + ClearEtherealV1 = 60, } impl JournalEntryRecordType { @@ -96,6 +97,11 @@ impl JournalEntryRecordType { JournalEntryRecordType::InitModuleV1 => ArchivedJournalEntry::InitModuleV1( rkyv::archived_root::(data), ), + JournalEntryRecordType::ClearEtherealV1 => { + ArchivedJournalEntry::ClearEtherealV1(rkyv::archived_root::< + JournalEntryClearEtherealV1, + >(data)) + } JournalEntryRecordType::ProcessExitV1 => ArchivedJournalEntry::ProcessExitV1( rkyv::archived_root::(data), ), @@ -342,6 +348,7 @@ impl<'a> JournalEntry<'a> { pub fn archive_record_type(&self) -> JournalEntryRecordType { match self { Self::InitModuleV1 { .. } => JournalEntryRecordType::InitModuleV1, + Self::ClearEtherealV1 { .. } => JournalEntryRecordType::ClearEtherealV1, Self::UpdateMemoryRegionV1 { .. } => JournalEntryRecordType::UpdateMemoryRegionV1, Self::ProcessExitV1 { .. } => JournalEntryRecordType::ProcessExitV1, Self::SetThreadV1 { .. } => JournalEntryRecordType::SetThreadV1, @@ -433,6 +440,9 @@ impl<'a> JournalEntry<'a> { JournalEntry::InitModuleV1 { wasm_hash } => { serializer.serialize_value(&JournalEntryInitModuleV1 { wasm_hash }) } + JournalEntry::ClearEtherealV1 => { + serializer.serialize_value(&JournalEntryClearEtherealV1 {}) + } JournalEntry::UpdateMemoryRegionV1 { region, data } => { serializer.serialize_value(&JournalEntryUpdateMemoryRegionV1 { start: region.start, @@ -875,6 +885,7 @@ pub(crate) struct JournalEntryHeader { pub enum ArchivedJournalEntry<'a> { InitModuleV1(&'a ArchivedJournalEntryInitModuleV1), + ClearEtherealV1(&'a ArchivedJournalEntryClearEtherealV1), ProcessExitV1(&'a ArchivedJournalEntryProcessExitV1), SetThreadV1(&'a ArchivedJournalEntrySetThreadV1<'a>), CloseThreadV1(&'a ArchivedJournalEntryCloseThreadV1), @@ -942,6 +953,12 @@ pub struct JournalEntryInitModuleV1 { pub wasm_hash: [u8; 8], } +#[repr(C)] +#[repr(align(8))] +#[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] +#[archive_attr(derive(CheckBytes), repr(align(8)))] +pub struct JournalEntryClearEtherealV1 {} + #[repr(C)] #[repr(align(8))] #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] diff --git a/lib/journal/src/concrete/archived_from.rs b/lib/journal/src/concrete/archived_from.rs index 4cbb645666f..4bd54513d0b 100644 --- a/lib/journal/src/concrete/archived_from.rs +++ b/lib/journal/src/concrete/archived_from.rs @@ -648,6 +648,9 @@ impl<'a> TryFrom> for JournalEntry<'a> { wasm_hash: *wasm_hash, } } + ArchivedJournalEntry::ClearEtherealV1(ArchivedJournalEntryClearEtherealV1 { + .. + }) => Self::ClearEtherealV1, ArchivedJournalEntry::UpdateMemoryRegionV1( ArchivedJournalEntryUpdateMemoryRegionV1 { start, diff --git a/lib/journal/src/concrete/filter.rs b/lib/journal/src/concrete/filter.rs index b89b2d9a6ba..f0bef520119 100644 --- a/lib/journal/src/concrete/filter.rs +++ b/lib/journal/src/concrete/filter.rs @@ -233,6 +233,7 @@ impl WritableJournal for FilteredJournalTx { } entry } + JournalEntry::ClearEtherealV1 => entry, JournalEntry::SetThreadV1 { .. } | JournalEntry::CloseThreadV1 { .. } => { if self.config.filter_threads { return Ok(LogWriteResult { diff --git a/lib/journal/src/concrete/printing.rs b/lib/journal/src/concrete/printing.rs index 2e6378a0424..baa29599149 100644 --- a/lib/journal/src/concrete/printing.rs +++ b/lib/journal/src/concrete/printing.rs @@ -68,6 +68,9 @@ impl<'a> fmt::Display for JournalEntry<'a> { JournalEntry::InitModuleV1 { wasm_hash } => { write!(f, "init-module (hash={:x?})", wasm_hash) } + JournalEntry::ClearEtherealV1 => { + write!(f, "clear-ethereal") + } JournalEntry::UpdateMemoryRegionV1 { region, data } => write!( f, "memory-update (start={}, end={}, data.len={})", diff --git a/lib/journal/src/entry.rs b/lib/journal/src/entry.rs index 24b79b344d9..29190d9d350 100644 --- a/lib/journal/src/entry.rs +++ b/lib/journal/src/entry.rs @@ -86,6 +86,7 @@ pub enum JournalEntry<'a> { InitModuleV1 { wasm_hash: [u8; 8], }, + ClearEtherealV1, UpdateMemoryRegionV1 { region: Range, #[derivative(Debug = "ignore")] @@ -374,6 +375,7 @@ impl<'a> JournalEntry<'a> { pub fn into_owned(self) -> JournalEntry<'static> { match self { Self::InitModuleV1 { wasm_hash } => JournalEntry::InitModuleV1 { wasm_hash }, + Self::ClearEtherealV1 => JournalEntry::ClearEtherealV1, Self::UpdateMemoryRegionV1 { region, data } => JournalEntry::UpdateMemoryRegionV1 { region, data: data.into_owned().into(), @@ -714,6 +716,7 @@ impl<'a> JournalEntry<'a> { let base_size = std::mem::size_of_val(self); match self { JournalEntry::InitModuleV1 { .. } => base_size, + JournalEntry::ClearEtherealV1 => base_size, JournalEntry::UpdateMemoryRegionV1 { data, .. } => base_size + data.len(), JournalEntry::ProcessExitV1 { .. } => base_size, JournalEntry::SetThreadV1 { diff --git a/lib/wasix/src/net/socket.rs b/lib/wasix/src/net/socket.rs index 813c8366427..166c1161946 100644 --- a/lib/wasix/src/net/socket.rs +++ b/lib/wasix/src/net/socket.rs @@ -408,8 +408,11 @@ impl InodeSocket { net.listen_tcp(addr, only_v6, reuse_port, reuse_addr) } - _ => { - tracing::warn!("wasi[?]::sock_listen - failed - not supported(1)"); + ty => { + tracing::warn!( + "wasi[?]::sock_listen - failed - not supported(pre-socket:{:?})", + ty + ); return Err(Errno::Notsup); } }, @@ -430,13 +433,34 @@ impl InodeSocket { net.listen_tcp(addr, only_v6, reuse_port, reuse_addr) } - _ => { - tracing::warn!("wasi[?]::sock_listen - failed - not supported(1)"); + ty => { + tracing::warn!( + "wasi[?]::sock_listen - failed - not supported(remote-socket:{:?})", + ty + ); return Err(Errno::Notsup); } }, - _ => { - tracing::warn!("wasi[?]::sock_listen - failed - not supported(2)"); + InodeSocketKind::Icmp(_) => { + tracing::warn!("wasi[?]::sock_listen - failed - not supported(icmp)"); + return Err(Errno::Notsup); + } + InodeSocketKind::Raw(_) => { + tracing::warn!("wasi[?]::sock_listen - failed - not supported(raw)"); + return Err(Errno::Notsup); + } + InodeSocketKind::TcpListener { .. } => { + tracing::warn!( + "wasi[?]::sock_listen - failed - already listening (tcp-listener)" + ); + return Err(Errno::Notsup); + } + InodeSocketKind::TcpStream { .. } => { + tracing::warn!("wasi[?]::sock_listen - failed - not supported(tcp-stream)"); + return Err(Errno::Notsup); + } + InodeSocketKind::UdpSocket { .. } => { + tracing::warn!("wasi[?]::sock_listen - failed - not supported(udp-socket)"); return Err(Errno::Notsup); } } diff --git a/lib/wasix/src/runners/dproxy/factory.rs b/lib/wasix/src/runners/dproxy/factory.rs index c7055684bc9..f5f492fbe9c 100644 --- a/lib/wasix/src/runners/dproxy/factory.rs +++ b/lib/wasix/src/runners/dproxy/factory.rs @@ -6,6 +6,7 @@ use std::{ }; use derivative::Derivative; +use wasmer_journal::{DynJournal, RecombinedJournal}; use crate::{ runners::Runner, @@ -57,7 +58,16 @@ impl DProxyInstanceFactory { // DProxy is able to resume execution of the stateful workload using memory // snapshots hence the journals it stores are complete journals - let journals = runtime.journals().clone().into_iter().collect::>(); + let journals = runtime + .journals() + .clone() + .into_iter() + .map(|journal| { + let tx = Box::new(journal.clone()); + let rx = journal.as_restarted()?; + anyhow::Result::Ok(Arc::new(RecombinedJournal::new(tx, rx)) as Arc) + }) + .collect::>>()?; let mut runtime = OverriddenRuntime::new(runtime).with_journals(journals); // We attach a composite networking to the runtime which includes a loopback diff --git a/lib/wasix/src/state/func_env.rs b/lib/wasix/src/state/func_env.rs index 4c28df65629..845c55a3ef3 100644 --- a/lib/wasix/src/state/func_env.rs +++ b/lib/wasix/src/state/func_env.rs @@ -292,7 +292,20 @@ impl WasiFunctionEnv { ) .map_err(|err| { WasiRuntimeError::Runtime(wasmer::RuntimeError::new(format!( - "journal failied to save the module initialization event - {}", + "journal failed to save the module initialization event - {}", + err + ))) + })?; + } else { + // Otherwise we should emit a clear ethereal event + let mut ctx = self.env.clone().into_mut(&mut store); + crate::journal::JournalEffector::save_event( + &mut ctx, + crate::journal::JournalEntry::ClearEtherealV1, + ) + .map_err(|err| { + WasiRuntimeError::Runtime(wasmer::RuntimeError::new(format!( + "journal failed to save clear ethereal event - {}", err ))) })?; diff --git a/lib/wasix/src/syscalls/journal.rs b/lib/wasix/src/syscalls/journal.rs deleted file mode 100644 index 3b1d389b242..00000000000 --- a/lib/wasix/src/syscalls/journal.rs +++ /dev/null @@ -1,845 +0,0 @@ -use super::*; - -#[allow(clippy::extra_unused_type_parameters)] -#[cfg(not(feature = "journal"))] -pub fn maybe_snapshot_once<'a, M: MemorySize>( - ctx: FunctionEnvMut<'a, WasiEnv>, - _trigger: crate::journal::SnapshotTrigger, -) -> WasiResult> { - Ok(Ok(ctx)) -} - -#[cfg(feature = "journal")] -pub fn maybe_snapshot_once<'a, M: MemorySize>( - mut ctx: FunctionEnvMut<'a, WasiEnv>, - trigger: crate::journal::SnapshotTrigger, -) -> WasiResult> { - use crate::os::task::process::{WasiProcessCheckpoint, WasiProcessInner}; - - if unsafe { handle_rewind_ext_with_default::(&mut ctx, HandleRewindType::ResultLess) } - .is_some() - { - return Ok(Ok(ctx)); - } - - if !ctx.data().enable_journal { - return Ok(Ok(ctx)); - } - - if ctx.data_mut().pop_snapshot_trigger(trigger) { - let inner = ctx.data().process.inner.clone(); - let res = wasi_try_ok_ok!(WasiProcessInner::checkpoint::( - inner, - ctx, - WasiProcessCheckpoint::Snapshot { trigger }, - )?); - match res { - MaybeCheckpointResult::Unwinding => return Ok(Err(Errno::Success)), - MaybeCheckpointResult::NotThisTime(c) => { - ctx = c; - } - } - } - Ok(Ok(ctx)) -} - -#[allow(clippy::extra_unused_type_parameters)] -#[cfg(not(feature = "journal"))] -pub fn maybe_snapshot_many<'a, M: MemorySize>( - ctx: FunctionEnvMut<'a, WasiEnv>, - _trigger: crate::journal::SnapshotTrigger, -) -> WasiResult> { - Ok(Ok(ctx)) -} - -#[cfg(feature = "journal")] -pub fn maybe_snapshot_many<'a, M: MemorySize>( - mut ctx: FunctionEnvMut<'a, WasiEnv>, - trigger: crate::journal::SnapshotTrigger, -) -> WasiResult> { - use crate::os::task::process::{WasiProcessCheckpoint, WasiProcessInner}; - - if unsafe { handle_rewind_ext_with_default::(&mut ctx, HandleRewindType::ResultLess) } - .is_some() - { - return Ok(Ok(ctx)); - } - - if !ctx.data().enable_journal { - return Ok(Ok(ctx)); - } - - if ctx.data_mut().has_snapshot_trigger(trigger) { - let inner = ctx.data().process.inner.clone(); - let res = wasi_try_ok_ok!(WasiProcessInner::checkpoint::( - inner, - ctx, - WasiProcessCheckpoint::Snapshot { trigger }, - )?); - match res { - MaybeCheckpointResult::Unwinding => return Ok(Err(Errno::Success)), - MaybeCheckpointResult::NotThisTime(c) => { - ctx = c; - } - } - } - Ok(Ok(ctx)) -} - -#[allow(clippy::extra_unused_type_parameters)] -#[cfg(not(feature = "journal"))] -pub fn maybe_snapshot<'a, M: MemorySize>( - ctx: FunctionEnvMut<'a, WasiEnv>, -) -> WasiResult> { - Ok(Ok(ctx)) -} - -#[cfg(feature = "journal")] -pub fn maybe_snapshot<'a, M: MemorySize>( - mut ctx: FunctionEnvMut<'a, WasiEnv>, -) -> WasiResult> { - use crate::os::task::process::{WasiProcessCheckpoint, WasiProcessInner}; - - if !ctx.data().enable_journal { - return Ok(Ok(ctx)); - } - - let inner = ctx.data().process.inner.clone(); - let res = wasi_try_ok_ok!(WasiProcessInner::maybe_checkpoint::(inner, ctx)?); - match res { - MaybeCheckpointResult::Unwinding => return Ok(Err(Errno::Success)), - MaybeCheckpointResult::NotThisTime(c) => { - ctx = c; - } - } - Ok(Ok(ctx)) -} - -#[cfg(not(feature = "journal"))] -pub fn wait_for_snapshot(_env: &WasiEnv) -> Pin>> { - Box::pin(std::future::pending()) -} - -#[cfg(feature = "journal")] -pub fn wait_for_snapshot(env: &WasiEnv) -> Pin>> { - use crate::os::task::process::{LockableWasiProcessInner, WasiProcessCheckpoint}; - - struct Poller { - inner: LockableWasiProcessInner, - } - impl Future for Poller { - type Output = (); - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut guard = self.inner.0.lock().unwrap(); - if !matches!(guard.checkpoint, WasiProcessCheckpoint::Execute) { - return Poll::Ready(()); - } - if !guard.wakers.iter().any(|w| w.will_wake(cx.waker())) { - guard.wakers.push(cx.waker().clone()); - } - Poll::Pending - } - } - Box::pin(Poller { - inner: env.process.inner.clone(), - }) -} - -/// Safety: This function manipulates the memory of the process and thus must -/// be executed by the WASM process thread itself. -/// -#[allow(clippy::result_large_err)] -#[cfg(feature = "journal")] -pub unsafe fn restore_snapshot( - mut ctx: FunctionEnvMut<'_, WasiEnv>, - journal: Arc, - bootstrapping: bool, -) -> Result, WasiRuntimeError> { - use std::{collections::BTreeMap, ops::Range}; - - use crate::{journal::Journal, os::task::process::MemorySnapshotRegion}; - - // We delay the spawning of threads until the end as its - // possible that the threads will be cancelled before all the - // events finished the streaming process - let mut spawn_threads: BTreeMap = Default::default(); - let mut staging_spawn_threads: BTreeMap = Default::default(); - let mut staging_kill_thread: HashSet = Default::default(); - - // We delay the memory updates until the end as its possible the - // memory will be cleared before all the events finished the - // streaming process - let mut update_memory: BTreeMap> = Default::default(); - let mut staging_update_memory: BTreeMap> = - Default::default(); - let mut ethereal_fds = HashSet::new(); - let mut update_tty = None; - - // We capture the stdout and stderr while we replay - let mut stdout = Vec::new(); - let mut stderr = Vec::new(); - let mut stdout_fds = HashSet::new(); - let mut stderr_fds = HashSet::new(); - stdout_fds.insert(1 as WasiFd); - stderr_fds.insert(2 as WasiFd); - - // Loop through all the events and process them - let cur_module_hash = ctx.data().process.module_hash.as_bytes(); - let mut journal_module_hash = None; - let mut rewind = None; - let mut staging_rewind = None; - while let Some(next) = journal.read().map_err(anyhow_err_to_runtime_err)? { - tracing::trace!("Restoring snapshot event - {next:?}"); - match next.into_inner() { - crate::journal::JournalEntry::InitModuleV1 { wasm_hash } => { - journal_module_hash.replace(wasm_hash); - } - crate::journal::JournalEntry::ProcessExitV1 { exit_code } => { - if bootstrapping { - rewind = None; - staging_rewind = None; - spawn_threads.clear(); - staging_spawn_threads.clear(); - update_memory.clear(); - staging_update_memory.clear(); - update_tty.take(); - stdout.clear(); - stderr.clear(); - stdout_fds.clear(); - stderr_fds.clear(); - stdout_fds.insert(1 as WasiFd); - stderr_fds.insert(2 as WasiFd); - for fd in ethereal_fds.drain() { - JournalEffector::apply_fd_close(&mut ctx, fd) - .map_err(anyhow_err_to_runtime_err)?; - } - } else { - JournalEffector::apply_process_exit(&mut ctx, exit_code) - .map_err(anyhow_err_to_runtime_err)?; - } - } - crate::journal::JournalEntry::FileDescriptorWriteV1 { - fd, - offset, - data, - is_64bit, - } => { - if stdout_fds.contains(&fd) { - stdout.push((offset, data, is_64bit)); - continue; - } - if stderr_fds.contains(&fd) { - stderr.push((offset, data, is_64bit)); - continue; - } - - if is_64bit { - JournalEffector::apply_fd_write::(&ctx, fd, offset, data) - } else { - JournalEffector::apply_fd_write::(&ctx, fd, offset, data) - } - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::FileDescriptorSeekV1 { fd, offset, whence } => { - JournalEffector::apply_fd_seek(&mut ctx, fd, offset, whence) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::UpdateMemoryRegionV1 { region, data } => { - if Some(cur_module_hash) != journal_module_hash { - continue; - } - - if bootstrapping { - staging_update_memory.insert(region.into(), data.clone()); - } else { - JournalEffector::apply_memory(&mut ctx, region, &data) - .map_err(anyhow_err_to_runtime_err)?; - } - } - crate::journal::JournalEntry::CloseThreadV1 { id, exit_code } => { - if id == ctx.data().tid().raw() { - if bootstrapping { - rewind = None; - staging_rewind = None; - spawn_threads.clear(); - staging_spawn_threads.clear(); - update_memory.clear(); - staging_update_memory.clear(); - update_tty.take(); - stdout.clear(); - stderr.clear(); - stdout_fds.clear(); - stderr_fds.clear(); - stdout_fds.insert(1 as WasiFd); - stderr_fds.insert(2 as WasiFd); - for fd in ethereal_fds.drain() { - JournalEffector::apply_fd_close(&mut ctx, fd) - .map_err(anyhow_err_to_runtime_err)?; - } - } else { - JournalEffector::apply_process_exit(&mut ctx, exit_code) - .map_err(anyhow_err_to_runtime_err)?; - } - } else if bootstrapping { - staging_spawn_threads.remove(&Into::::into(id)); - staging_kill_thread.insert(id.into()); - } else { - JournalEffector::apply_thread_exit( - &mut ctx, - Into::::into(id), - exit_code, - ) - .map_err(anyhow_err_to_runtime_err)?; - } - } - crate::journal::JournalEntry::SetThreadV1 { - id, - call_stack, - memory_stack, - store_data, - is_64bit, - start, - layout, - } => { - if Some(cur_module_hash) != journal_module_hash { - continue; - } - - let state = RewindState { - memory_stack: memory_stack.to_vec().into(), - rewind_stack: call_stack.to_vec().into(), - store_data: store_data.to_vec().into(), - start, - layout, - is_64bit, - }; - - let id = Into::::into(id); - if id == ctx.data().tid() { - staging_rewind.replace(state); - } else if bootstrapping { - staging_kill_thread.remove(&id); - staging_spawn_threads.insert(id, state); - } else { - return Err(WasiRuntimeError::Runtime(RuntimeError::user( - anyhow::format_err!( - "Snapshot restoration does not currently support live updates of running threads." - ) - .into(), - ))); - } - } - crate::journal::JournalEntry::CloseFileDescriptorV1 { fd } => { - ethereal_fds.remove(&fd); - stdout_fds.remove(&fd); - stderr_fds.remove(&fd); - JournalEffector::apply_fd_close(&mut ctx, fd).map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::OpenFileDescriptorV1 { - fd, - dirfd, - dirflags, - path, - o_flags, - fs_rights_base, - fs_rights_inheriting, - fs_flags, - } => { - JournalEffector::apply_path_open( - &mut ctx, - fd, - dirfd, - dirflags, - &path, - o_flags, - fs_rights_base, - fs_rights_inheriting, - fs_flags, - ) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::RemoveDirectoryV1 { fd, path } => { - JournalEffector::apply_path_remove_directory(&mut ctx, fd, &path) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::UnlinkFileV1 { fd, path } => { - JournalEffector::apply_path_unlink(&mut ctx, fd, &path) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::PathRenameV1 { - old_fd, - old_path, - new_fd, - new_path, - } => { - JournalEffector::apply_path_rename(&mut ctx, old_fd, &old_path, new_fd, &new_path) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::SnapshotV1 { when: _, trigger } => { - if Some(cur_module_hash) != journal_module_hash { - continue; - } - - if let Some(new_rewind) = staging_rewind.take() { - rewind.replace(new_rewind); - } - for thread_id in staging_kill_thread.drain() { - spawn_threads.remove(&thread_id); - } - while let Some(thread) = staging_spawn_threads.pop_first() { - spawn_threads.insert(thread.0, thread.1); - } - while let Some(mem) = staging_update_memory.pop_first() { - update_memory.insert(mem.0, mem.1); - } - - ctx.data_mut().pop_snapshot_trigger(trigger); - } - crate::journal::JournalEntry::SetClockTimeV1 { clock_id, time } => { - JournalEffector::apply_clock_time_set(&mut ctx, clock_id, time) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::RenumberFileDescriptorV1 { old_fd, new_fd } => { - if old_fd != new_fd { - stdout_fds.remove(&new_fd); - stderr_fds.remove(&new_fd); - } - if stdout_fds.remove(&old_fd) { - stdout_fds.insert(new_fd); - } - if stderr_fds.remove(&old_fd) { - stderr_fds.insert(new_fd); - } - if ethereal_fds.remove(&old_fd) { - ethereal_fds.insert(new_fd); - } - JournalEffector::apply_fd_renumber(&mut ctx, old_fd, new_fd) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::DuplicateFileDescriptorV1 { - original_fd, - copied_fd, - } => { - if original_fd != copied_fd { - stdout_fds.remove(&copied_fd); - stderr_fds.remove(&copied_fd); - } - if stdout_fds.contains(&original_fd) { - stdout_fds.insert(copied_fd); - } - if stderr_fds.contains(&original_fd) { - stderr_fds.insert(copied_fd); - } - if ethereal_fds.contains(&original_fd) { - ethereal_fds.insert(copied_fd); - } - JournalEffector::apply_fd_duplicate(&mut ctx, original_fd, copied_fd) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::CreateDirectoryV1 { fd, path } => { - JournalEffector::apply_path_create_directory(&mut ctx, fd, &path) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::PathSetTimesV1 { - fd, - flags, - path, - st_atim, - st_mtim, - fst_flags, - } => { - JournalEffector::apply_path_set_times( - &mut ctx, fd, flags, &path, st_atim, st_mtim, fst_flags, - ) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::FileDescriptorSetTimesV1 { - fd, - st_atim, - st_mtim, - fst_flags, - } => { - JournalEffector::apply_fd_set_times(&mut ctx, fd, st_atim, st_mtim, fst_flags) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::FileDescriptorSetSizeV1 { fd, st_size } => { - JournalEffector::apply_fd_set_size(&mut ctx, fd, st_size) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::FileDescriptorSetFlagsV1 { fd, flags } => { - JournalEffector::apply_fd_set_flags(&mut ctx, fd, flags) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::FileDescriptorSetRightsV1 { - fd, - fs_rights_base, - fs_rights_inheriting, - } => { - JournalEffector::apply_fd_set_rights( - &mut ctx, - fd, - fs_rights_base, - fs_rights_inheriting, - ) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::FileDescriptorAdviseV1 { - fd, - offset, - len, - advice, - } => { - JournalEffector::apply_fd_advise(&mut ctx, fd, offset, len, advice) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::FileDescriptorAllocateV1 { fd, offset, len } => { - JournalEffector::apply_fd_allocate(&mut ctx, fd, offset, len) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::CreateHardLinkV1 { - old_fd, - old_path, - old_flags, - new_fd, - new_path, - } => { - JournalEffector::apply_path_link( - &mut ctx, old_fd, old_flags, &old_path, new_fd, &new_path, - ) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::CreateSymbolicLinkV1 { - old_path, - fd, - new_path, - } => { - JournalEffector::apply_path_symlink(&mut ctx, &old_path, fd, &new_path) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::ChangeDirectoryV1 { path } => { - JournalEffector::apply_chdir(&mut ctx, &path).map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::CreatePipeV1 { fd1, fd2 } => { - ethereal_fds.insert(fd1); - ethereal_fds.insert(fd2); - JournalEffector::apply_fd_pipe(&mut ctx, fd1, fd2) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::EpollCreateV1 { fd } => { - ethereal_fds.insert(fd); - JournalEffector::apply_epoll_create(&mut ctx, fd) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::EpollCtlV1 { - epfd, - op, - fd, - event, - } => { - JournalEffector::apply_epoll_ctl(&mut ctx, epfd, op, fd, event) - .map_err(anyhow_err_to_runtime_err)?; - } - crate::journal::JournalEntry::TtySetV1 { tty, line_feeds } => { - let state = crate::WasiTtyState { - cols: tty.cols, - rows: tty.rows, - width: tty.width, - height: tty.height, - stdin_tty: tty.stdin_tty, - stdout_tty: tty.stdout_tty, - stderr_tty: tty.stderr_tty, - echo: tty.echo, - line_buffered: tty.line_buffered, - line_feeds, - }; - - if bootstrapping { - update_tty.replace(state); - } else { - JournalEffector::apply_tty_set(&mut ctx, state) - .map_err(anyhow_err_to_runtime_err)?; - } - } - crate::journal::JournalEntry::PortAddAddrV1 { cidr } => { - JournalEffector::apply_port_addr_add(&mut ctx, cidr) - .map_err(anyhow_err_to_runtime_err)? - } - crate::journal::JournalEntry::PortDelAddrV1 { addr } => { - JournalEffector::apply_port_addr_remove(&mut ctx, addr) - .map_err(anyhow_err_to_runtime_err)? - } - crate::journal::JournalEntry::PortAddrClearV1 => { - JournalEffector::apply_port_addr_clear(&mut ctx) - .map_err(anyhow_err_to_runtime_err)? - } - crate::journal::JournalEntry::PortBridgeV1 { - network, - token, - security, - } => JournalEffector::apply_port_bridge(&mut ctx, &network, &token, security) - .map_err(anyhow_err_to_runtime_err)?, - crate::journal::JournalEntry::PortUnbridgeV1 => { - JournalEffector::apply_port_unbridge(&mut ctx).map_err(anyhow_err_to_runtime_err)? - } - crate::journal::JournalEntry::PortDhcpAcquireV1 => { - JournalEffector::apply_port_dhcp_acquire(&mut ctx) - .map_err(anyhow_err_to_runtime_err)? - } - crate::journal::JournalEntry::PortGatewaySetV1 { ip } => { - JournalEffector::apply_port_gateway_set(&mut ctx, ip) - .map_err(anyhow_err_to_runtime_err)? - } - crate::journal::JournalEntry::PortRouteAddV1 { - cidr, - via_router, - preferred_until, - expires_at, - } => JournalEffector::apply_port_route_add( - &mut ctx, - cidr, - via_router, - preferred_until, - expires_at, - ) - .map_err(anyhow_err_to_runtime_err)?, - crate::journal::JournalEntry::PortRouteClearV1 => { - JournalEffector::apply_port_route_clear(&mut ctx) - .map_err(anyhow_err_to_runtime_err)? - } - crate::journal::JournalEntry::PortRouteDelV1 { ip } => { - JournalEffector::apply_port_route_remove(&mut ctx, ip) - .map_err(anyhow_err_to_runtime_err)? - } - crate::journal::JournalEntry::SocketOpenV1 { af, ty, pt, fd } => { - ethereal_fds.insert(fd); - JournalEffector::apply_sock_open(&mut ctx, af, ty, pt, fd) - .map_err(anyhow_err_to_runtime_err)? - } - crate::journal::JournalEntry::SocketListenV1 { fd, backlog } => { - JournalEffector::apply_sock_listen(&mut ctx, fd, backlog as usize) - .map_err(anyhow_err_to_runtime_err)? - } - crate::journal::JournalEntry::SocketBindV1 { fd, addr } => { - JournalEffector::apply_sock_bind(&mut ctx, fd, addr) - .map_err(anyhow_err_to_runtime_err)? - } - crate::journal::JournalEntry::SocketConnectedV1 { - fd, - local_addr, - peer_addr, - } => JournalEffector::apply_sock_connect(&mut ctx, fd, local_addr, peer_addr) - .map_err(anyhow_err_to_runtime_err)?, - crate::journal::JournalEntry::SocketAcceptedV1 { - listen_fd, - fd, - local_addr: addr, - peer_addr, - fd_flags, - non_blocking: nonblocking, - } => { - ethereal_fds.insert(fd); - JournalEffector::apply_sock_accepted( - &mut ctx, - listen_fd, - fd, - addr, - peer_addr, - fd_flags, - nonblocking, - ) - .map_err(anyhow_err_to_runtime_err)? - } - crate::journal::JournalEntry::SocketJoinIpv4MulticastV1 { - fd, - multiaddr, - iface, - } => JournalEffector::apply_sock_join_ipv4_multicast(&mut ctx, fd, multiaddr, iface) - .map_err(anyhow_err_to_runtime_err)?, - crate::journal::JournalEntry::SocketJoinIpv6MulticastV1 { - fd, - multi_addr: multiaddr, - iface, - } => JournalEffector::apply_sock_join_ipv6_multicast(&mut ctx, fd, multiaddr, iface) - .map_err(anyhow_err_to_runtime_err)?, - crate::journal::JournalEntry::SocketLeaveIpv4MulticastV1 { - fd, - multi_addr: multiaddr, - iface, - } => JournalEffector::apply_sock_leave_ipv4_multicast(&mut ctx, fd, multiaddr, iface) - .map_err(anyhow_err_to_runtime_err)?, - crate::journal::JournalEntry::SocketLeaveIpv6MulticastV1 { - fd, - multi_addr: multiaddr, - iface, - } => JournalEffector::apply_sock_leave_ipv6_multicast(&mut ctx, fd, multiaddr, iface) - .map_err(anyhow_err_to_runtime_err)?, - crate::journal::JournalEntry::SocketSendFileV1 { - socket_fd, - file_fd, - offset, - count, - } => JournalEffector::apply_sock_send_file(&mut ctx, socket_fd, file_fd, offset, count) - .map_err(anyhow_err_to_runtime_err)?, - crate::journal::JournalEntry::SocketSendToV1 { - fd, - data, - flags, - addr, - is_64bit, - } => if is_64bit { - JournalEffector::apply_sock_send_to::(&ctx, fd, data, flags, addr) - } else { - JournalEffector::apply_sock_send_to::(&ctx, fd, data, flags, addr) - } - .map_err(anyhow_err_to_runtime_err)?, - crate::journal::JournalEntry::SocketSendV1 { - fd, - data, - flags, - is_64bit, - } => if is_64bit { - JournalEffector::apply_sock_send::(&ctx, fd, data, flags) - } else { - JournalEffector::apply_sock_send::(&ctx, fd, data, flags) - } - .map_err(anyhow_err_to_runtime_err)?, - crate::journal::JournalEntry::SocketSetOptFlagV1 { fd, opt, flag } => { - JournalEffector::apply_sock_set_opt_flag(&mut ctx, fd, opt, flag) - .map_err(anyhow_err_to_runtime_err)? - } - crate::journal::JournalEntry::SocketSetOptSizeV1 { fd, opt, size } => { - JournalEffector::apply_sock_set_opt_size(&mut ctx, fd, opt, size) - .map_err(anyhow_err_to_runtime_err)? - } - crate::journal::JournalEntry::SocketSetOptTimeV1 { fd, ty, time } => { - JournalEffector::apply_sock_set_opt_time(&mut ctx, fd, ty.into(), time) - .map_err(anyhow_err_to_runtime_err)? - } - crate::journal::JournalEntry::SocketShutdownV1 { fd, how } => { - JournalEffector::apply_sock_shutdown(&mut ctx, fd, how.into()) - .map_err(anyhow_err_to_runtime_err)? - } - crate::journal::JournalEntry::CreateEventV1 { - initial_val, - flags, - fd, - } => { - ethereal_fds.insert(fd); - JournalEffector::apply_fd_event(&mut ctx, initial_val, flags, fd) - .map_err(anyhow_err_to_runtime_err)? - } - } - } - - // Check for events that are orphaned - if !staging_kill_thread.is_empty() { - tracing::debug!("Orphaned thread exit (missing snapshot) - it will be ignored"); - } - if !staging_update_memory.is_empty() { - tracing::debug!("Orphaned memory update (missing snapshot) - it will be ignored"); - } - if !staging_spawn_threads.is_empty() { - tracing::debug!("Orphaned thread spawn (missing snapshot) - it will be ignored"); - } - if staging_rewind.is_some() { - tracing::debug!("Orphaned main rewind (missing snapshot) - it will be ignored"); - } - - // If we are not in the same module then we fire off an exit - // that simulates closing the process (hence keeps everything - // in a clean state) - if journal_module_hash.is_some() && Some(cur_module_hash) != journal_module_hash { - tracing::error!( - "The WASM module hash does not match the journal module hash (journal_hash={:x?} vs module_hash{:x?}) - forcing a restart", - journal_module_hash.unwrap(), - cur_module_hash - ); - if bootstrapping { - rewind = None; - staging_rewind = None; - spawn_threads.clear(); - staging_spawn_threads.clear(); - update_memory.clear(); - staging_update_memory.clear(); - update_tty.take(); - stdout.clear(); - stderr.clear(); - stdout_fds.clear(); - stderr_fds.clear(); - stdout_fds.insert(1 as WasiFd); - stderr_fds.insert(2 as WasiFd); - for fd in ethereal_fds.drain() { - JournalEffector::apply_fd_close(&mut ctx, fd).map_err(anyhow_err_to_runtime_err)?; - } - } else { - JournalEffector::apply_process_exit(&mut ctx, None) - .map_err(anyhow_err_to_runtime_err)?; - } - } - - // Now output the stdout and stderr - for (offset, data, is_64bit) in stdout { - if is_64bit { - JournalEffector::apply_fd_write::(&ctx, 1, offset, data) - } else { - JournalEffector::apply_fd_write::(&ctx, 1, offset, data) - } - .map_err(anyhow_err_to_runtime_err)?; - } - - for (offset, data, is_64bit) in stderr { - if is_64bit { - JournalEffector::apply_fd_write::(&ctx, 2, offset, data) - } else { - JournalEffector::apply_fd_write::(&ctx, 2, offset, data) - } - .map_err(anyhow_err_to_runtime_err)?; - } - - // Next we apply all the memory updates that were delayed while the logs - // were processed to completion. - for (region, data) in update_memory { - JournalEffector::apply_memory(&mut ctx, region.into(), &data) - .map_err(anyhow_err_to_runtime_err)?; - } - - if let Some(state) = update_tty { - JournalEffector::apply_tty_set(&mut ctx, state).map_err(anyhow_err_to_runtime_err)?; - } - - // If the main thread is not being restored then don't bother - // attempting to restore the spawned threads either as the - // main process is effectively in an exited state - if rewind.is_none() { - spawn_threads.clear(); - } - - // Spawn all the threads - for (thread_id, thread_state) in spawn_threads { - if thread_state.is_64bit { - JournalEffector::apply_thread_state::( - &mut ctx, - thread_id, - thread_state.memory_stack, - thread_state.rewind_stack, - thread_state.store_data, - thread_state.start, - thread_state.layout, - ) - .map_err(anyhow_err_to_runtime_err)?; - } else { - JournalEffector::apply_thread_state::( - &mut ctx, - thread_id, - thread_state.memory_stack, - thread_state.rewind_stack, - thread_state.store_data, - thread_state.start, - thread_state.layout, - ) - .map_err(anyhow_err_to_runtime_err)?; - } - } - - Ok(rewind) -} diff --git a/lib/wasix/src/syscalls/journal/actions/close_thread.rs b/lib/wasix/src/syscalls/journal/actions/close_thread.rs new file mode 100644 index 00000000000..3c20cf88e02 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/close_thread.rs @@ -0,0 +1,33 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_close_thread( + &mut self, + id: u32, + exit_code: Option, + differ_ethereal: Option<&mut Vec>>, + ) -> Result<(), WasiRuntimeError> { + tracing::trace!(%id, ?exit_code, "Replay journal - CloseThread"); + if id == self.ctx.data().tid().raw() { + if self.bootstrapping { + self.clear_ethereal(differ_ethereal); + self.staged_differ_memory.clear(); + self.differ_memory.clear(); + self.rewind = None; + } else { + JournalEffector::apply_process_exit(&mut self.ctx, exit_code) + .map_err(anyhow_err_to_runtime_err)?; + } + } else if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::CloseThreadV1 { id, exit_code }); + } else { + JournalEffector::apply_thread_exit( + &mut self.ctx, + Into::::into(id), + exit_code, + ) + .map_err(anyhow_err_to_runtime_err)?; + } + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/actions/fd_advise.rs b/lib/wasix/src/syscalls/journal/actions/fd_advise.rs new file mode 100644 index 00000000000..b3d40dec2ed --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/fd_advise.rs @@ -0,0 +1,16 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_fd_advise( + &mut self, + fd: Fd, + offset: Filesize, + len: Filesize, + advice: Advice, + ) -> Result<(), WasiRuntimeError> { + tracing::trace!(%fd, %offset, %len, ?advice, "Replay journal - FdAdvise"); + JournalEffector::apply_fd_advise(&mut self.ctx, fd, offset, len, advice) + .map_err(anyhow_err_to_runtime_err)?; + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/actions/fd_allocate.rs b/lib/wasix/src/syscalls/journal/actions/fd_allocate.rs new file mode 100644 index 00000000000..0831bcdef70 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/fd_allocate.rs @@ -0,0 +1,15 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_fd_allocate( + &mut self, + fd: Fd, + offset: Filesize, + len: Filesize, + ) -> Result<(), WasiRuntimeError> { + tracing::trace!(%fd, %offset, %len, "Replay journal - FdAllocate"); + JournalEffector::apply_fd_allocate(&mut self.ctx, fd, offset, len) + .map_err(anyhow_err_to_runtime_err)?; + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/actions/fd_close.rs b/lib/wasix/src/syscalls/journal/actions/fd_close.rs new file mode 100644 index 00000000000..23cad8ff0fa --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/fd_close.rs @@ -0,0 +1,11 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_fd_close(&mut self, fd: u32) -> Result<(), WasiRuntimeError> { + tracing::trace!(%fd, "Replay journal - FdClose"); + self.stdout_fds.remove(&fd); + self.stderr_fds.remove(&fd); + JournalEffector::apply_fd_close(&mut self.ctx, fd).map_err(anyhow_err_to_runtime_err)?; + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/actions/fd_dup.rs b/lib/wasix/src/syscalls/journal/actions/fd_dup.rs new file mode 100644 index 00000000000..a3ad9d75691 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/fd_dup.rs @@ -0,0 +1,25 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_fd_dup( + &mut self, + original_fd: u32, + copied_fd: u32, + ) -> Result<(), WasiRuntimeError> { + tracing::trace!(%original_fd, %copied_fd, "Replay journal - FdDuplicate"); + self.real_fd.insert(copied_fd); + if original_fd != copied_fd { + self.stdout_fds.remove(&copied_fd); + self.stderr_fds.remove(&copied_fd); + } + if self.stdout_fds.contains(&original_fd) { + self.stdout_fds.insert(copied_fd); + } + if self.stderr_fds.contains(&original_fd) { + self.stderr_fds.insert(copied_fd); + } + JournalEffector::apply_fd_duplicate(&mut self.ctx, original_fd, copied_fd) + .map_err(anyhow_err_to_runtime_err)?; + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/actions/fd_open.rs b/lib/wasix/src/syscalls/journal/actions/fd_open.rs new file mode 100644 index 00000000000..aa48c6b8286 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/fd_open.rs @@ -0,0 +1,30 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_fd_open( + &mut self, + fd: u32, + dirfd: u32, + dirflags: u32, + path: Cow<'a, str>, + o_flags: Oflags, + fs_rights_base: Rights, + fs_rights_inheriting: Rights, + fs_flags: Fdflags, + ) -> Result<(), WasiRuntimeError> { + tracing::trace!(%fd, %dirfd, %dirflags, "Replay journal - FdOpen {}", path); + JournalEffector::apply_path_open( + &mut self.ctx, + fd, + dirfd, + dirflags, + &path, + o_flags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + ) + .map_err(anyhow_err_to_runtime_err)?; + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/actions/fd_renumber.rs b/lib/wasix/src/syscalls/journal/actions/fd_renumber.rs new file mode 100644 index 00000000000..d1adb0fc24d --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/fd_renumber.rs @@ -0,0 +1,25 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_fd_renumber( + &mut self, + old_fd: u32, + new_fd: u32, + ) -> Result<(), WasiRuntimeError> { + tracing::trace!(%old_fd, %new_fd, "Replay journal - FdRenumber"); + self.real_fd.insert(new_fd); + if old_fd != new_fd { + self.stdout_fds.remove(&new_fd); + self.stderr_fds.remove(&new_fd); + } + if self.stdout_fds.remove(&old_fd) { + self.stdout_fds.insert(new_fd); + } + if self.stderr_fds.remove(&old_fd) { + self.stderr_fds.insert(new_fd); + } + JournalEffector::apply_fd_renumber(&mut self.ctx, old_fd, new_fd) + .map_err(anyhow_err_to_runtime_err)?; + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/actions/fd_seek.rs b/lib/wasix/src/syscalls/journal/actions/fd_seek.rs new file mode 100644 index 00000000000..0545dedd205 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/fd_seek.rs @@ -0,0 +1,15 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_fd_seek( + &mut self, + fd: u32, + offset: i64, + whence: Whence, + ) -> Result<(), WasiRuntimeError> { + tracing::trace!(%fd, %offset, ?whence, "Replay journal - FdSeek"); + JournalEffector::apply_fd_seek(&mut self.ctx, fd, offset, whence) + .map_err(anyhow_err_to_runtime_err)?; + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/actions/fd_set_flags.rs b/lib/wasix/src/syscalls/journal/actions/fd_set_flags.rs new file mode 100644 index 00000000000..932a224be43 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/fd_set_flags.rs @@ -0,0 +1,14 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_fd_set_flags( + &mut self, + fd: Fd, + flags: Fdflags, + ) -> Result<(), WasiRuntimeError> { + tracing::trace!(%fd, ?flags, "Replay journal - FdSetFlags"); + JournalEffector::apply_fd_set_flags(&mut self.ctx, fd, flags) + .map_err(anyhow_err_to_runtime_err)?; + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/actions/fd_set_rights.rs b/lib/wasix/src/syscalls/journal/actions/fd_set_rights.rs new file mode 100644 index 00000000000..54d3601c22f --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/fd_set_rights.rs @@ -0,0 +1,20 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_fd_set_rights( + &mut self, + fd: Fd, + fs_rights_base: Rights, + fs_rights_inheriting: Rights, + ) -> Result<(), WasiRuntimeError> { + tracing::trace!(%fd, "Replay journal - FdSetRights"); + JournalEffector::apply_fd_set_rights( + &mut self.ctx, + fd, + fs_rights_base, + fs_rights_inheriting, + ) + .map_err(anyhow_err_to_runtime_err)?; + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/actions/fd_set_size.rs b/lib/wasix/src/syscalls/journal/actions/fd_set_size.rs new file mode 100644 index 00000000000..46f940edbaa --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/fd_set_size.rs @@ -0,0 +1,14 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_fd_set_size( + &mut self, + fd: Fd, + st_size: Filesize, + ) -> Result<(), WasiRuntimeError> { + tracing::trace!(%fd, %st_size, "Replay journal - FdSetSize"); + JournalEffector::apply_fd_set_size(&mut self.ctx, fd, st_size) + .map_err(anyhow_err_to_runtime_err)?; + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/actions/fd_set_times.rs b/lib/wasix/src/syscalls/journal/actions/fd_set_times.rs new file mode 100644 index 00000000000..fe7964efffc --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/fd_set_times.rs @@ -0,0 +1,16 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_fd_set_times( + &mut self, + fd: Fd, + st_atim: Timestamp, + st_mtim: Timestamp, + fst_flags: Fstflags, + ) -> Result<(), WasiRuntimeError> { + tracing::trace!(%fd, %st_atim, %st_mtim, ?fst_flags, "Replay journal - FdSetTimes"); + JournalEffector::apply_fd_set_times(&mut self.ctx, fd, st_atim, st_mtim, fst_flags) + .map_err(anyhow_err_to_runtime_err)?; + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/actions/fd_write.rs b/lib/wasix/src/syscalls/journal/actions/fd_write.rs new file mode 100644 index 00000000000..4af6fdb91ec --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/fd_write.rs @@ -0,0 +1,29 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_fd_write( + &mut self, + fd: u32, + offset: u64, + data: Cow<'a, [u8]>, + is_64bit: bool, + ) -> Result<(), WasiRuntimeError> { + tracing::trace!(%fd, %offset, "Replay journal - FdWrite"); + if self.stdout_fds.contains(&fd) { + self.stdout.push((offset, data, is_64bit)); + return Ok(()); + } + if self.stderr_fds.contains(&fd) { + self.stderr.push((offset, data, is_64bit)); + return Ok(()); + } + + if is_64bit { + JournalEffector::apply_fd_write::(&self.ctx, fd, offset, data) + } else { + JournalEffector::apply_fd_write::(&self.ctx, fd, offset, data) + } + .map_err(anyhow_err_to_runtime_err)?; + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/actions/init_module.rs b/lib/wasix/src/syscalls/journal/actions/init_module.rs new file mode 100644 index 00000000000..696b3079080 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/init_module.rs @@ -0,0 +1,15 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_init_module( + &mut self, + wasm_hash: [u8; 8], + differ_ethereal: Option<&mut Vec>>, + ) -> Result<(), WasiRuntimeError> { + tracing::trace!("Replay journal - InitModule {:?}", wasm_hash); + self.clear_ethereal(differ_ethereal); + self.differ_memory.clear(); + self.journal_module_hash.replace(wasm_hash); + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/actions/mod.rs b/lib/wasix/src/syscalls/journal/actions/mod.rs new file mode 100644 index 00000000000..fb8e0a8e305 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/mod.rs @@ -0,0 +1,46 @@ +mod close_thread; +mod fd_advise; +mod fd_allocate; +mod fd_close; +mod fd_dup; +mod fd_open; +mod fd_renumber; +mod fd_seek; +mod fd_set_flags; +mod fd_set_rights; +mod fd_set_times; +mod fd_write; +mod init_module; +mod path_set_times; +mod process_exit; +mod set_thread; +mod snapshot; +mod tty_set; +mod update_memory; +mod fd_set_size; + +use crate::journal::JournalEffector; +use crate::syscalls::anyhow_err_to_runtime_err; +use crate::syscalls::JournalReplayRunner; +use crate::RewindState; +use crate::WasiRuntimeError; +use crate::WasiThreadId; +use std::borrow::Cow; +use std::ops::Range; +use std::time::SystemTime; +use wasmer::RuntimeError; +use wasmer_journal::JournalEntry; +use wasmer_journal::SnapshotTrigger; +use wasmer_types::Memory32; +use wasmer_types::Memory64; +use wasmer_wasix_types::wasi::Advice; +use wasmer_wasix_types::wasi::ExitCode; +use wasmer_wasix_types::wasi::Fd; +use wasmer_wasix_types::wasi::Fdflags; +use wasmer_wasix_types::wasi::Filesize; +use wasmer_wasix_types::wasi::Tty; +use wasmer_wasix_types::wasi::Whence; +use wasmer_wasix_types::wasi::{Fstflags, LookupFlags, Timestamp}; +use wasmer_wasix_types::wasi::{Oflags, Rights}; +use wasmer_wasix_types::wasix::ThreadStartType; +use wasmer_wasix_types::wasix::WasiMemoryLayout; diff --git a/lib/wasix/src/syscalls/journal/actions/path_set_times.rs b/lib/wasix/src/syscalls/journal/actions/path_set_times.rs new file mode 100644 index 00000000000..299e6115929 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/path_set_times.rs @@ -0,0 +1,26 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_path_set_times( + &mut self, + fd: Fd, + flags: LookupFlags, + path: Cow<'_, str>, + st_atim: Timestamp, + st_mtim: Timestamp, + fst_flags: Fstflags, + ) -> Result<(), WasiRuntimeError> { + tracing::trace!(%fd, "Replay journal - PathSetTimes"); + JournalEffector::apply_path_set_times( + &mut self.ctx, + fd, + flags, + &path, + st_atim, + st_mtim, + fst_flags, + ) + .map_err(anyhow_err_to_runtime_err)?; + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/actions/process_exit.rs b/lib/wasix/src/syscalls/journal/actions/process_exit.rs new file mode 100644 index 00000000000..a797b6e9534 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/process_exit.rs @@ -0,0 +1,20 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_process_exit( + &mut self, + exit_code: Option, + differ_ethereal: Option<&mut Vec>>, + ) -> Result<(), WasiRuntimeError> { + tracing::trace!(?exit_code, "Replay journal - ProcessExit"); + if self.bootstrapping { + self.clear_ethereal(differ_ethereal); + self.differ_memory.clear(); + self.rewind = None; + } else { + JournalEffector::apply_process_exit(&mut self.ctx, exit_code) + .map_err(anyhow_err_to_runtime_err)?; + } + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/actions/set_thread.rs b/lib/wasix/src/syscalls/journal/actions/set_thread.rs new file mode 100644 index 00000000000..fae5edd90a1 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/set_thread.rs @@ -0,0 +1,51 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_set_thread( + &mut self, + id: u32, + call_stack: Cow<'a, [u8]>, + memory_stack: Cow<'a, [u8]>, + store_data: Cow<'a, [u8]>, + is_64bit: bool, + start: ThreadStartType, + layout: WasiMemoryLayout, + differ_ethereal: Option<&mut Vec>>, + ) -> Result<(), WasiRuntimeError> { + tracing::trace!(%id, "Replay journal - SetThread call_stack={} bytes memory_stack={} bytes store_data={} bytes", call_stack.len(), memory_stack.len(), store_data.len()); + if Some(self.cur_module_hash) != self.journal_module_hash { + return Ok(()); + } + + let state = RewindState { + memory_stack: memory_stack.to_vec().into(), + rewind_stack: call_stack.to_vec().into(), + store_data: store_data.to_vec().into(), + start, + layout: layout.clone(), + is_64bit, + }; + + if Into::::into(id) == self.ctx.data().tid() { + self.rewind.replace(state); + } else if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::SetThreadV1 { + id, + call_stack, + memory_stack, + store_data, + start, + layout, + is_64bit, + }); + } else { + return Err(WasiRuntimeError::Runtime(RuntimeError::user( + anyhow::format_err!( + "Snapshot restoration does not currently support live updates of running threads." + ) + .into(), + ))); + } + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/actions/snapshot.rs b/lib/wasix/src/syscalls/journal/actions/snapshot.rs new file mode 100644 index 00000000000..34f912fea32 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/snapshot.rs @@ -0,0 +1,42 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_snapshot( + &mut self, + when: SystemTime, + trigger: SnapshotTrigger, + differ_ethereal: Option<&mut Vec>>, + ) -> Result<(), WasiRuntimeError> { + tracing::trace!("Replay journal - Snapshot"); + + // If we are not in the same module then we fire off an exit + // that simulates closing the process (hence keeps everything + // in a clean state) + let mut clear_ethereal = false; + if self.journal_module_hash.is_some() + && Some(self.cur_module_hash) != self.journal_module_hash + { + tracing::error!( + "The WASM module hash does not match the journal module hash (journal_hash={:x?} vs module_hash{:x?}) - forcing a restart", + self.journal_module_hash.unwrap(), + self.cur_module_hash + ); + self.clear_ethereal(differ_ethereal); + return Ok(()); + } + + // Execute all the ethereal events + if let Some(ethereal_events) = differ_ethereal { + for next in ethereal_events.drain(..) { + tracing::trace!("Ethereal snapshot event - {next:?}"); + self.play_event(next, None)?; + } + for (region, data) in self.staged_differ_memory.drain(..) { + self.differ_memory.push((region, data)); + } + } + + self.ctx.data_mut().pop_snapshot_trigger(trigger); + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/actions/tty_set.rs b/lib/wasix/src/syscalls/journal/actions/tty_set.rs new file mode 100644 index 00000000000..65406396272 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/tty_set.rs @@ -0,0 +1,26 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_tty_set( + &mut self, + tty: Tty, + line_feeds: bool, + ) -> Result<(), WasiRuntimeError> { + tracing::trace!("Replay journal - TtySet"); + let state = crate::WasiTtyState { + cols: tty.cols, + rows: tty.rows, + width: tty.width, + height: tty.height, + stdin_tty: tty.stdin_tty, + stdout_tty: tty.stdout_tty, + stderr_tty: tty.stderr_tty, + echo: tty.echo, + line_buffered: tty.line_buffered, + line_feeds, + }; + + JournalEffector::apply_tty_set(&mut self.ctx, state).map_err(anyhow_err_to_runtime_err)?; + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/actions/update_memory.rs b/lib/wasix/src/syscalls/journal/actions/update_memory.rs new file mode 100644 index 00000000000..37f5b897b0c --- /dev/null +++ b/lib/wasix/src/syscalls/journal/actions/update_memory.rs @@ -0,0 +1,25 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(crate) unsafe fn action_update_memory( + &mut self, + region: Range, + data: Cow<'a, [u8]>, + differ_ethereal: Option<&mut Vec>>, + ) -> Result<(), WasiRuntimeError> { + tracing::trace!("Replay journal - UpdateMemory"); + if Some(self.cur_module_hash) != self.journal_module_hash { + return Ok(()); + } + + if self.bootstrapping { + self.staged_differ_memory.push((region, data)); + } else if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::UpdateMemoryRegionV1 { region, data }); + } else { + JournalEffector::apply_memory(&mut self.ctx, region, &data) + .map_err(anyhow_err_to_runtime_err)?; + } + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/clear_ethereal.rs b/lib/wasix/src/syscalls/journal/clear_ethereal.rs new file mode 100644 index 00000000000..c934cbb44f7 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/clear_ethereal.rs @@ -0,0 +1,18 @@ +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(super) fn clear_ethereal( + &mut self, + mut differ_ethereal: Option<&mut Vec>>, + ) { + self.spawn_threads.clear(); + self.stdout.clear(); + self.stderr.clear(); + self.stdout_fds.clear(); + self.stderr_fds.clear(); + self.stdout_fds.insert(1 as WasiFd); + self.stderr_fds.insert(2 as WasiFd); + differ_ethereal.iter_mut().for_each(|e| e.clear()); + self.staged_differ_memory.clear(); + } +} diff --git a/lib/wasix/src/syscalls/journal/maybe_snapshot.rs b/lib/wasix/src/syscalls/journal/maybe_snapshot.rs new file mode 100644 index 00000000000..1dd7bdd2fc0 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/maybe_snapshot.rs @@ -0,0 +1,30 @@ +use super::*; + +#[allow(clippy::extra_unused_type_parameters)] +#[cfg(not(feature = "journal"))] +pub fn maybe_snapshot<'a, M: MemorySize>( + ctx: FunctionEnvMut<'a, WasiEnv>, +) -> WasiResult> { + Ok(Ok(ctx)) +} + +#[cfg(feature = "journal")] +pub fn maybe_snapshot<'a, M: MemorySize>( + mut ctx: FunctionEnvMut<'a, WasiEnv>, +) -> WasiResult> { + use crate::os::task::process::{WasiProcessCheckpoint, WasiProcessInner}; + + if !ctx.data().enable_journal { + return Ok(Ok(ctx)); + } + + let inner = ctx.data().process.inner.clone(); + let res = wasi_try_ok_ok!(WasiProcessInner::maybe_checkpoint::(inner, ctx)?); + match res { + MaybeCheckpointResult::Unwinding => return Ok(Err(Errno::Success)), + MaybeCheckpointResult::NotThisTime(c) => { + ctx = c; + } + } + Ok(Ok(ctx)) +} diff --git a/lib/wasix/src/syscalls/journal/maybe_snapshot_many.rs b/lib/wasix/src/syscalls/journal/maybe_snapshot_many.rs new file mode 100644 index 00000000000..ec032978912 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/maybe_snapshot_many.rs @@ -0,0 +1,44 @@ +use super::*; + +#[allow(clippy::extra_unused_type_parameters)] +#[cfg(not(feature = "journal"))] +pub fn maybe_snapshot_many<'a, M: MemorySize>( + ctx: FunctionEnvMut<'a, WasiEnv>, + _trigger: crate::journal::SnapshotTrigger, +) -> WasiResult> { + Ok(Ok(ctx)) +} + +#[cfg(feature = "journal")] +pub fn maybe_snapshot_many<'a, M: MemorySize>( + mut ctx: FunctionEnvMut<'a, WasiEnv>, + trigger: crate::journal::SnapshotTrigger, +) -> WasiResult> { + use crate::os::task::process::{WasiProcessCheckpoint, WasiProcessInner}; + + if unsafe { handle_rewind_ext_with_default::(&mut ctx, HandleRewindType::ResultLess) } + .is_some() + { + return Ok(Ok(ctx)); + } + + if !ctx.data().enable_journal { + return Ok(Ok(ctx)); + } + + if ctx.data_mut().has_snapshot_trigger(trigger) { + let inner = ctx.data().process.inner.clone(); + let res = wasi_try_ok_ok!(WasiProcessInner::checkpoint::( + inner, + ctx, + WasiProcessCheckpoint::Snapshot { trigger }, + )?); + match res { + MaybeCheckpointResult::Unwinding => return Ok(Err(Errno::Success)), + MaybeCheckpointResult::NotThisTime(c) => { + ctx = c; + } + } + } + Ok(Ok(ctx)) +} diff --git a/lib/wasix/src/syscalls/journal/maybe_snapshot_once.rs b/lib/wasix/src/syscalls/journal/maybe_snapshot_once.rs new file mode 100644 index 00000000000..45399730036 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/maybe_snapshot_once.rs @@ -0,0 +1,44 @@ +use super::*; + +#[allow(clippy::extra_unused_type_parameters)] +#[cfg(not(feature = "journal"))] +pub fn maybe_snapshot_once<'a, M: MemorySize>( + ctx: FunctionEnvMut<'a, WasiEnv>, + _trigger: crate::journal::SnapshotTrigger, +) -> WasiResult> { + Ok(Ok(ctx)) +} + +#[cfg(feature = "journal")] +pub fn maybe_snapshot_once<'a, M: MemorySize>( + mut ctx: FunctionEnvMut<'a, WasiEnv>, + trigger: crate::journal::SnapshotTrigger, +) -> WasiResult> { + use crate::os::task::process::{WasiProcessCheckpoint, WasiProcessInner}; + + if unsafe { handle_rewind_ext_with_default::(&mut ctx, HandleRewindType::ResultLess) } + .is_some() + { + return Ok(Ok(ctx)); + } + + if !ctx.data().enable_journal { + return Ok(Ok(ctx)); + } + + if ctx.data_mut().pop_snapshot_trigger(trigger) { + let inner = ctx.data().process.inner.clone(); + let res = wasi_try_ok_ok!(WasiProcessInner::checkpoint::( + inner, + ctx, + WasiProcessCheckpoint::Snapshot { trigger }, + )?); + match res { + MaybeCheckpointResult::Unwinding => return Ok(Err(Errno::Success)), + MaybeCheckpointResult::NotThisTime(c) => { + ctx = c; + } + } + } + Ok(Ok(ctx)) +} diff --git a/lib/wasix/src/syscalls/journal/mod.rs b/lib/wasix/src/syscalls/journal/mod.rs new file mode 100644 index 00000000000..10e4eeaa4f2 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/mod.rs @@ -0,0 +1,78 @@ +mod actions; +mod clear_ethereal; +mod maybe_snapshot; +mod maybe_snapshot_many; +mod maybe_snapshot_once; +mod play_event; +mod restore_snapshot; +mod wait_for_snapshot; + +use actions::*; +use clear_ethereal::*; +use maybe_snapshot::*; +use maybe_snapshot_many::*; +use maybe_snapshot_once::*; +use restore_snapshot::*; +use wait_for_snapshot::*; +use wasmer_journal::JournalEntry; + +pub use maybe_snapshot::*; +pub use maybe_snapshot_many::*; +pub use maybe_snapshot_once::*; +pub use restore_snapshot::*; +pub use wait_for_snapshot::*; + +use crate::os::task::process::MemorySnapshotRegion; +use std::{collections::BTreeMap, ops::Range}; + +use super::*; + +pub struct JournalReplayRunner<'a, 'c> { + pub ctx: FunctionEnvMut<'c, WasiEnv>, + pub bootstrapping: bool, + + pub journal_module_hash: Option<[u8; 8]>, + pub rewind: Option, + pub cur_module_hash: [u8; 8], + pub real_fd: HashSet, + + // We delay the spawning of threads until the end as its + // possible that the threads will be cancelled before all the + // events finished the streaming process + pub spawn_threads: BTreeMap, + pub staged_differ_memory: Vec<(Range, Cow<'a, [u8]>)>, + pub differ_memory: Vec<(Range, Cow<'a, [u8]>)>, + + // We capture the stdout and stderr while we replay + pub stdout: Vec<(u64, Cow<'a, [u8]>, bool)>, + pub stderr: Vec<(u64, Cow<'a, [u8]>, bool)>, + pub stdout_fds: HashSet, + pub stderr_fds: HashSet, +} + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub fn new(mut ctx: FunctionEnvMut<'c, WasiEnv>, bootstrapping: bool) -> Self { + let cur_module_hash: [u8; 8] = ctx.data().process.module_hash.as_bytes(); + let mut ret = JournalReplayRunner { + ctx, + bootstrapping, + cur_module_hash, + journal_module_hash: None, + rewind: None, + spawn_threads: Default::default(), + staged_differ_memory: Default::default(), + differ_memory: Default::default(), + stdout: Default::default(), + stderr: Default::default(), + stdout_fds: Default::default(), + stderr_fds: Default::default(), + real_fd: Default::default(), + }; + + // We capture the stdout and stderr while we replay + ret.stdout_fds.insert(1 as WasiFd); + ret.stderr_fds.insert(2 as WasiFd); + + ret + } +} diff --git a/lib/wasix/src/syscalls/journal/play_event.rs b/lib/wasix/src/syscalls/journal/play_event.rs new file mode 100644 index 00000000000..0815cfbfdac --- /dev/null +++ b/lib/wasix/src/syscalls/journal/play_event.rs @@ -0,0 +1,714 @@ +use std::ops::Range; + +use super::*; + +impl<'a, 'c> JournalReplayRunner<'a, 'c> { + pub(super) unsafe fn play_event( + &mut self, + next: JournalEntry<'a>, + differ_ethereal: Option<&mut Vec>>, + ) -> Result<(), WasiRuntimeError> { + match next { + JournalEntry::InitModuleV1 { wasm_hash } => { + self.action_init_module(wasm_hash, differ_ethereal)?; + } + JournalEntry::ClearEtherealV1 => { + self.clear_ethereal(differ_ethereal); + } + JournalEntry::ProcessExitV1 { exit_code } => { + self.action_process_exit(exit_code, differ_ethereal)?; + } + JournalEntry::FileDescriptorWriteV1 { + fd, + offset, + data, + is_64bit, + } => { + if self.real_fd.contains(&fd) { + self.action_fd_write(fd, offset, data, is_64bit)?; + } else if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::FileDescriptorWriteV1 { + fd, + offset, + data, + is_64bit, + }); + } else { + self.action_fd_write(fd, offset, data, is_64bit)?; + } + } + JournalEntry::FileDescriptorSeekV1 { fd, offset, whence } => { + if self.real_fd.contains(&fd) { + self.action_fd_seek(fd, offset, whence)?; + } else if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::FileDescriptorSeekV1 { fd, offset, whence }); + } else { + self.action_fd_seek(fd, offset, whence)?; + } + } + JournalEntry::UpdateMemoryRegionV1 { region, data } => { + self.action_update_memory(region, data, differ_ethereal)?; + } + JournalEntry::CloseThreadV1 { id, exit_code } => { + self.action_close_thread(id, exit_code, differ_ethereal)?; + } + JournalEntry::SetThreadV1 { + id, + call_stack, + memory_stack, + store_data, + is_64bit, + start, + layout, + } => { + self.action_set_thread( + id, + call_stack, + memory_stack, + store_data, + is_64bit, + start, + layout, + differ_ethereal, + )?; + } + JournalEntry::CloseFileDescriptorV1 { fd } => { + if self.real_fd.contains(&fd) { + self.action_fd_close(fd)?; + } else if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::CloseFileDescriptorV1 { fd }); + } else { + self.action_fd_close(fd)?; + } + } + JournalEntry::OpenFileDescriptorV1 { + fd, + dirfd, + dirflags, + path, + o_flags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + } => { + self.real_fd.insert(fd); + self.action_fd_open( + fd, + dirfd, + dirflags, + path, + o_flags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + )?; + } + JournalEntry::RemoveDirectoryV1 { fd, path } => { + tracing::trace!("Replay journal - RemoveDirectory {}", path); + JournalEffector::apply_path_remove_directory(&mut self.ctx, fd, &path) + .map_err(anyhow_err_to_runtime_err)?; + } + JournalEntry::UnlinkFileV1 { fd, path } => { + tracing::trace!("Replay journal - UnlinkFile {}", path); + JournalEffector::apply_path_unlink(&mut self.ctx, fd, &path) + .map_err(anyhow_err_to_runtime_err)?; + } + JournalEntry::PathRenameV1 { + old_fd, + old_path, + new_fd, + new_path, + } => { + tracing::trace!("Replay journal - PathRename {}->{}", old_path, new_path); + JournalEffector::apply_path_rename( + &mut self.ctx, + old_fd, + &old_path, + new_fd, + &new_path, + ) + .map_err(anyhow_err_to_runtime_err)?; + } + JournalEntry::SnapshotV1 { when, trigger } => { + self.action_snapshot(when, trigger, differ_ethereal)?; + } + JournalEntry::SetClockTimeV1 { clock_id, time } => { + tracing::trace!(?clock_id, %time, "Replay journal - ClockTimeSet"); + JournalEffector::apply_clock_time_set(&mut self.ctx, clock_id, time) + .map_err(anyhow_err_to_runtime_err)?; + } + JournalEntry::RenumberFileDescriptorV1 { old_fd, new_fd } => { + if self.real_fd.remove(&old_fd) { + self.action_fd_renumber(old_fd, new_fd)?; + } else if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::RenumberFileDescriptorV1 { old_fd, new_fd }); + } else { + self.action_fd_renumber(old_fd, new_fd)?; + } + } + JournalEntry::DuplicateFileDescriptorV1 { + original_fd, + copied_fd, + } => { + if self.real_fd.contains(&original_fd) { + self.action_fd_dup(original_fd, copied_fd)?; + } else if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::DuplicateFileDescriptorV1 { + original_fd, + copied_fd, + }); + } else { + self.action_fd_dup(original_fd, copied_fd)?; + } + } + JournalEntry::CreateDirectoryV1 { fd, path } => { + tracing::trace!(%fd, %path, "Replay journal - CreateDirectory"); + JournalEffector::apply_path_create_directory(&mut self.ctx, fd, &path) + .map_err(anyhow_err_to_runtime_err)?; + } + JournalEntry::PathSetTimesV1 { + fd, + flags, + path, + st_atim, + st_mtim, + fst_flags, + } => { + if self.real_fd.contains(&fd) { + self.action_path_set_times(fd, flags, path, st_atim, st_mtim, fst_flags)?; + } else if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::PathSetTimesV1 { + fd, + flags, + path, + st_atim, + st_mtim, + fst_flags, + }); + } else { + self.action_path_set_times(fd, flags, path, st_atim, st_mtim, fst_flags)?; + } + } + JournalEntry::FileDescriptorSetTimesV1 { + fd, + st_atim, + st_mtim, + fst_flags, + } => { + if self.real_fd.contains(&fd) { + self.action_fd_set_times(fd, st_atim, st_mtim, fst_flags)? + } else if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::FileDescriptorSetTimesV1 { + fd, + st_atim, + st_mtim, + fst_flags, + }); + } else { + self.action_fd_set_times(fd, st_atim, st_mtim, fst_flags)? + } + } + JournalEntry::FileDescriptorSetSizeV1 { fd, st_size } => { + if self.real_fd.contains(&fd) { + self.action_fd_set_size(fd, st_size)?; + } else if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::FileDescriptorSetSizeV1 { fd, st_size }); + } else { + self.action_fd_set_size(fd, st_size)?; + } + } + JournalEntry::FileDescriptorSetFlagsV1 { fd, flags } => { + if self.real_fd.contains(&fd) { + self.action_fd_set_flags(fd, flags)?; + } else if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::FileDescriptorSetFlagsV1 { fd, flags }); + } else { + self.action_fd_set_flags(fd, flags)?; + } + } + JournalEntry::FileDescriptorSetRightsV1 { + fd, + fs_rights_base, + fs_rights_inheriting, + } => { + if self.real_fd.contains(&fd) { + self.action_fd_set_rights(fd, fs_rights_base, fs_rights_inheriting)?; + } else if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::FileDescriptorSetRightsV1 { + fd, + fs_rights_base, + fs_rights_inheriting, + }); + } else { + self.action_fd_set_rights(fd, fs_rights_base, fs_rights_inheriting)?; + } + } + JournalEntry::FileDescriptorAdviseV1 { + fd, + offset, + len, + advice, + } => { + if self.real_fd.contains(&fd) { + self.action_fd_advise(fd, offset, len, advice)?; + } else if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::FileDescriptorAdviseV1 { + fd, + offset, + len, + advice, + }); + } else { + self.action_fd_advise(fd, offset, len, advice)?; + } + } + JournalEntry::FileDescriptorAllocateV1 { fd, offset, len } => { + if self.real_fd.contains(&fd) { + self.action_fd_allocate(fd, offset, len)?; + } else if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::FileDescriptorAllocateV1 { + fd, + offset, + len, + }); + } else { + self.action_fd_allocate(fd, offset, len)?; + } + } + JournalEntry::CreateHardLinkV1 { + old_fd, + old_path, + old_flags, + new_fd, + new_path, + } => { + tracing::trace!("Replay journal - PathLink {}->{}", old_path, new_path); + JournalEffector::apply_path_link( + &mut self.ctx, + old_fd, + old_flags, + &old_path, + new_fd, + &new_path, + ) + .map_err(anyhow_err_to_runtime_err)?; + } + JournalEntry::CreateSymbolicLinkV1 { + old_path, + fd, + new_path, + } => { + tracing::trace!("Replay journal - PathSymlink {}->{}", old_path, new_path); + JournalEffector::apply_path_symlink(&mut self.ctx, &old_path, fd, &new_path) + .map_err(anyhow_err_to_runtime_err)?; + } + JournalEntry::ChangeDirectoryV1 { path } => { + JournalEffector::apply_chdir(&mut self.ctx, &path) + .map_err(anyhow_err_to_runtime_err)?; + } + JournalEntry::CreatePipeV1 { fd1, fd2 } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::CreatePipeV1 { fd1, fd2 }); + } else { + tracing::trace!(%fd1, %fd2, "Replay journal - CreatePipe"); + JournalEffector::apply_fd_pipe(&mut self.ctx, fd1, fd2) + .map_err(anyhow_err_to_runtime_err)?; + } + } + JournalEntry::EpollCreateV1 { fd } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::EpollCreateV1 { fd }); + } else { + tracing::trace!(%fd, "Replay journal - EpollCreate"); + JournalEffector::apply_epoll_create(&mut self.ctx, fd) + .map_err(anyhow_err_to_runtime_err)?; + } + } + JournalEntry::EpollCtlV1 { + epfd, + op, + fd, + event, + } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::EpollCtlV1 { + epfd, + op, + fd, + event, + }); + } else { + tracing::trace!(%epfd, %fd, ?op, "Replay journal - EpollCtl"); + JournalEffector::apply_epoll_ctl(&mut self.ctx, epfd, op, fd, event) + .map_err(anyhow_err_to_runtime_err)?; + } + } + JournalEntry::TtySetV1 { tty, line_feeds } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::TtySetV1 { tty, line_feeds }); + } else { + self.action_tty_set(tty, line_feeds)?; + } + } + JournalEntry::PortAddAddrV1 { cidr } => { + tracing::trace!(?cidr, "Replay journal - PortAddAddr"); + JournalEffector::apply_port_addr_add(&mut self.ctx, cidr) + .map_err(anyhow_err_to_runtime_err)? + } + JournalEntry::PortDelAddrV1 { addr } => { + tracing::trace!(?addr, "Replay journal - PortDelAddr"); + JournalEffector::apply_port_addr_remove(&mut self.ctx, addr) + .map_err(anyhow_err_to_runtime_err)? + } + JournalEntry::PortAddrClearV1 => { + tracing::trace!("Replay journal - PortAddrClear"); + JournalEffector::apply_port_addr_clear(&mut self.ctx) + .map_err(anyhow_err_to_runtime_err)? + } + JournalEntry::PortBridgeV1 { + network, + token, + security, + } => { + tracing::trace!("Replay journal - PortBridge"); + JournalEffector::apply_port_bridge(&mut self.ctx, &network, &token, security) + .map_err(anyhow_err_to_runtime_err)? + } + JournalEntry::PortUnbridgeV1 => { + tracing::trace!("Replay journal - PortUnBridge"); + JournalEffector::apply_port_unbridge(&mut self.ctx) + .map_err(anyhow_err_to_runtime_err)? + } + JournalEntry::PortDhcpAcquireV1 => { + tracing::trace!("Replay journal - PortDhcpAcquire"); + JournalEffector::apply_port_dhcp_acquire(&mut self.ctx) + .map_err(anyhow_err_to_runtime_err)? + } + JournalEntry::PortGatewaySetV1 { ip } => { + tracing::trace!(?ip, "Replay journal - PortGatewaySet"); + JournalEffector::apply_port_gateway_set(&mut self.ctx, ip) + .map_err(anyhow_err_to_runtime_err)? + } + JournalEntry::PortRouteAddV1 { + cidr, + via_router, + preferred_until, + expires_at, + } => { + tracing::trace!(?cidr, "Replay journal - PortRouteAdd"); + JournalEffector::apply_port_route_add( + &mut self.ctx, + cidr, + via_router, + preferred_until, + expires_at, + ) + .map_err(anyhow_err_to_runtime_err)? + } + JournalEntry::PortRouteClearV1 => { + tracing::trace!("Replay journal - PortRouteClear"); + JournalEffector::apply_port_route_clear(&mut self.ctx) + .map_err(anyhow_err_to_runtime_err)? + } + JournalEntry::PortRouteDelV1 { ip } => { + tracing::trace!(?ip, "Replay journal - PortRouteDel"); + JournalEffector::apply_port_route_remove(&mut self.ctx, ip) + .map_err(anyhow_err_to_runtime_err)? + } + JournalEntry::SocketOpenV1 { af, ty, pt, fd } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::SocketOpenV1 { af, ty, pt, fd }); + } else { + tracing::trace!(?af, ?ty, ?pt, %fd, "Replay journal - SocketOpen"); + JournalEffector::apply_sock_open(&mut self.ctx, af, ty, pt, fd) + .map_err(anyhow_err_to_runtime_err)? + } + } + JournalEntry::SocketListenV1 { fd, backlog } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::SocketListenV1 { fd, backlog }); + } else { + tracing::trace!(%fd, "Replay journal - SocketListen"); + JournalEffector::apply_sock_listen(&mut self.ctx, fd, backlog as usize) + .map_err(anyhow_err_to_runtime_err)? + } + } + JournalEntry::SocketBindV1 { fd, addr } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::SocketBindV1 { fd, addr }); + } else { + tracing::trace!(%fd, ?addr, "Replay journal - SocketBind"); + JournalEffector::apply_sock_bind(&mut self.ctx, fd, addr) + .map_err(anyhow_err_to_runtime_err)? + } + } + JournalEntry::SocketConnectedV1 { + fd, + local_addr, + peer_addr, + } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::SocketConnectedV1 { + fd, + local_addr, + peer_addr, + }); + } else { + tracing::trace!(%fd, ?peer_addr, "Replay journal - SockConnect"); + JournalEffector::apply_sock_connect(&mut self.ctx, fd, local_addr, peer_addr) + .map_err(anyhow_err_to_runtime_err)? + } + } + JournalEntry::SocketAcceptedV1 { + listen_fd, + fd, + local_addr: addr, + peer_addr, + fd_flags, + non_blocking: nonblocking, + } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::SocketAcceptedV1 { + listen_fd, + fd, + local_addr: addr, + peer_addr, + fd_flags, + non_blocking: nonblocking, + }); + } else { + tracing::trace!(%listen_fd, %fd, ?peer_addr, "Replay journal - SocketAccept"); + JournalEffector::apply_sock_accepted( + &mut self.ctx, + listen_fd, + fd, + addr, + peer_addr, + fd_flags, + nonblocking, + ) + .map_err(anyhow_err_to_runtime_err)? + } + } + JournalEntry::SocketJoinIpv4MulticastV1 { + fd, + multiaddr, + iface, + } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::SocketJoinIpv4MulticastV1 { + fd, + multiaddr, + iface, + }); + } else { + tracing::trace!(%fd, ?multiaddr, "Replay journal - JoinIpv4Multicast"); + JournalEffector::apply_sock_join_ipv4_multicast( + &mut self.ctx, + fd, + multiaddr, + iface, + ) + .map_err(anyhow_err_to_runtime_err)? + } + } + JournalEntry::SocketJoinIpv6MulticastV1 { + fd, + multi_addr: multiaddr, + iface, + } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::SocketJoinIpv6MulticastV1 { + fd, + multi_addr: multiaddr, + iface, + }); + } else { + tracing::trace!(%fd, ?multiaddr, "Replay journal - JoinIpv6Multicast"); + JournalEffector::apply_sock_join_ipv6_multicast( + &mut self.ctx, + fd, + multiaddr, + iface, + ) + .map_err(anyhow_err_to_runtime_err)? + } + } + JournalEntry::SocketLeaveIpv4MulticastV1 { + fd, + multi_addr: multiaddr, + iface, + } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::SocketLeaveIpv4MulticastV1 { + fd, + multi_addr: multiaddr, + iface, + }); + } else { + tracing::trace!(%fd, ?multiaddr, "Replay journal - LeaveIpv4Multicast"); + JournalEffector::apply_sock_leave_ipv4_multicast( + &mut self.ctx, + fd, + multiaddr, + iface, + ) + .map_err(anyhow_err_to_runtime_err)? + } + } + JournalEntry::SocketLeaveIpv6MulticastV1 { + fd, + multi_addr: multiaddr, + iface, + } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::SocketLeaveIpv6MulticastV1 { + fd, + multi_addr: multiaddr, + iface, + }); + } else { + tracing::trace!(%fd, ?multiaddr, "Replay journal - LeaveIpv6Multicast"); + JournalEffector::apply_sock_leave_ipv6_multicast( + &mut self.ctx, + fd, + multiaddr, + iface, + ) + .map_err(anyhow_err_to_runtime_err)? + } + } + JournalEntry::SocketSendFileV1 { + socket_fd, + file_fd, + offset, + count, + } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::SocketSendFileV1 { + socket_fd, + file_fd, + offset, + count, + }); + } else { + tracing::trace!(%socket_fd, %file_fd, %offset, %count, "Replay journal - SockSendFile"); + JournalEffector::apply_sock_send_file( + &mut self.ctx, + socket_fd, + file_fd, + offset, + count, + ) + .map_err(anyhow_err_to_runtime_err)? + } + } + JournalEntry::SocketSendToV1 { + fd, + data, + flags, + addr, + is_64bit, + } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::SocketSendToV1 { + fd, + data, + flags, + addr, + is_64bit, + }); + } else { + tracing::trace!(%fd, "Replay journal - SocketSendTo data={} bytes", data.len()); + if is_64bit { + JournalEffector::apply_sock_send_to::( + &self.ctx, fd, data, flags, addr, + ) + } else { + JournalEffector::apply_sock_send_to::( + &self.ctx, fd, data, flags, addr, + ) + } + .map_err(anyhow_err_to_runtime_err)? + } + } + JournalEntry::SocketSendV1 { + fd, + data, + flags, + is_64bit, + } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::SocketSendV1 { + fd, + data, + flags, + is_64bit, + }); + } else { + tracing::trace!(%fd, "Replay journal - SocketSend data={} bytes", data.len()); + if is_64bit { + JournalEffector::apply_sock_send::(&self.ctx, fd, data, flags) + } else { + JournalEffector::apply_sock_send::(&self.ctx, fd, data, flags) + } + .map_err(anyhow_err_to_runtime_err)? + } + } + JournalEntry::SocketSetOptFlagV1 { fd, opt, flag } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::SocketSetOptFlagV1 { fd, opt, flag }); + } else { + tracing::trace!(%fd, ?opt, %flag, "Replay journal - SocketSetOptFlag"); + JournalEffector::apply_sock_set_opt_flag(&mut self.ctx, fd, opt, flag) + .map_err(anyhow_err_to_runtime_err)? + } + } + JournalEntry::SocketSetOptSizeV1 { fd, opt, size } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::SocketSetOptSizeV1 { fd, opt, size }); + } else { + tracing::trace!(%fd, ?opt, %size, "Replay journal - SocketSetOptSize"); + JournalEffector::apply_sock_set_opt_size(&mut self.ctx, fd, opt, size) + .map_err(anyhow_err_to_runtime_err)? + } + } + JournalEntry::SocketSetOptTimeV1 { fd, ty, time } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::SocketSetOptTimeV1 { fd, ty, time }); + } else { + tracing::trace!(%fd, ?ty, ?time, "Replay journal - SocketSetOptTime"); + JournalEffector::apply_sock_set_opt_time(&mut self.ctx, fd, ty.into(), time) + .map_err(anyhow_err_to_runtime_err)? + } + } + JournalEntry::SocketShutdownV1 { fd, how } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::SocketShutdownV1 { fd, how }); + } else { + tracing::trace!(%fd, ?how, "Replay journal - SocketShutdown"); + JournalEffector::apply_sock_shutdown(&mut self.ctx, fd, how.into()) + .map_err(anyhow_err_to_runtime_err)? + } + } + JournalEntry::CreateEventV1 { + initial_val, + flags, + fd, + } => { + if let Some(differ_ethereal) = differ_ethereal { + differ_ethereal.push(JournalEntry::CreateEventV1 { + initial_val, + flags, + fd, + }); + } else { + tracing::trace!(%fd, %flags, "Replay journal - CreateEvent"); + JournalEffector::apply_fd_event(&mut self.ctx, initial_val, flags, fd) + .map_err(anyhow_err_to_runtime_err)? + } + } + } + Ok(()) + } +} diff --git a/lib/wasix/src/syscalls/journal/restore_snapshot.rs b/lib/wasix/src/syscalls/journal/restore_snapshot.rs new file mode 100644 index 00000000000..e2026f14014 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/restore_snapshot.rs @@ -0,0 +1,89 @@ +use super::*; + +/// Safety: This function manipulates the memory of the process and thus must +/// be executed by the WASM process thread itself. +/// +#[allow(clippy::result_large_err)] +#[cfg(feature = "journal")] +pub unsafe fn restore_snapshot( + mut ctx: FunctionEnvMut<'_, WasiEnv>, + journal: Arc, + bootstrapping: bool, +) -> Result, WasiRuntimeError> { + use std::{collections::BTreeMap, ops::Range}; + + use crate::{journal::Journal, os::task::process::MemorySnapshotRegion}; + + // Create the journal replay runner + let mut runner = JournalReplayRunner::new(ctx, bootstrapping); + + // We read all the logs from the journal into the state machine + let mut ethereal_events = Vec::new(); + while let Some(next) = journal.read().map_err(anyhow_err_to_runtime_err)? { + runner.play_event(next.into_inner(), Some(&mut ethereal_events)); + } + + // Check for events that are orphaned + for evt in ethereal_events { + tracing::debug!("Orphaned ethereal events - {:?}", evt); + } + + // Now output the stdout and stderr + for (offset, data, is_64bit) in runner.stdout { + if is_64bit { + JournalEffector::apply_fd_write::(&runner.ctx, 1, offset, data) + } else { + JournalEffector::apply_fd_write::(&runner.ctx, 1, offset, data) + } + .map_err(anyhow_err_to_runtime_err)?; + } + + for (offset, data, is_64bit) in runner.stderr { + if is_64bit { + JournalEffector::apply_fd_write::(&runner.ctx, 2, offset, data) + } else { + JournalEffector::apply_fd_write::(&runner.ctx, 2, offset, data) + } + .map_err(anyhow_err_to_runtime_err)?; + } + + // Apply the memory changes (if this is in bootstrapping mode we differed them) + for (region, data) in runner.differ_memory { + tracing::trace!( + "Replay journal - UpdateMemory - region:{:?}, data.len={}", + region, + data.len() + ); + JournalEffector::apply_memory(&mut runner.ctx, region, &data) + .map_err(anyhow_err_to_runtime_err)?; + } + + // Spawn all the threads + for (thread_id, thread_state) in runner.spawn_threads { + if thread_state.is_64bit { + JournalEffector::apply_thread_state::( + &mut runner.ctx, + thread_id, + thread_state.memory_stack, + thread_state.rewind_stack, + thread_state.store_data, + thread_state.start, + thread_state.layout, + ) + .map_err(anyhow_err_to_runtime_err)?; + } else { + JournalEffector::apply_thread_state::( + &mut runner.ctx, + thread_id, + thread_state.memory_stack, + thread_state.rewind_stack, + thread_state.store_data, + thread_state.start, + thread_state.layout, + ) + .map_err(anyhow_err_to_runtime_err)?; + } + } + + Ok(runner.rewind) +} diff --git a/lib/wasix/src/syscalls/journal/wait_for_snapshot.rs b/lib/wasix/src/syscalls/journal/wait_for_snapshot.rs new file mode 100644 index 00000000000..856df4a21a8 --- /dev/null +++ b/lib/wasix/src/syscalls/journal/wait_for_snapshot.rs @@ -0,0 +1,31 @@ +use super::*; + +#[cfg(not(feature = "journal"))] +pub fn wait_for_snapshot(_env: &WasiEnv) -> Pin>> { + Box::pin(std::future::pending()) +} + +#[cfg(feature = "journal")] +pub fn wait_for_snapshot(env: &WasiEnv) -> Pin>> { + use crate::os::task::process::{LockableWasiProcessInner, WasiProcessCheckpoint}; + + struct Poller { + inner: LockableWasiProcessInner, + } + impl Future for Poller { + type Output = (); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut guard = self.inner.0.lock().unwrap(); + if !matches!(guard.checkpoint, WasiProcessCheckpoint::Execute) { + return Poll::Ready(()); + } + if !guard.wakers.iter().any(|w| w.will_wake(cx.waker())) { + guard.wakers.push(cx.waker().clone()); + } + Poll::Pending + } + } + Box::pin(Poller { + inner: env.process.inner.clone(), + }) +} From de2606d07961e9e7d6667ac9f9c9a84dda06cede Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 00:41:59 +1100 Subject: [PATCH 44/67] Correctly connected up the signal process for the ctrl-c key --- Cargo.lock | 32 ++++++++ lib/journal/src/concrete/archived.rs | 1 + lib/journal/src/concrete/archived_from.rs | 3 + lib/journal/src/snapshot.rs | 10 ++- lib/wasix/Cargo.toml | 3 + lib/wasix/src/os/task/process.rs | 95 ++++++++++++++--------- lib/wasix/src/os/task/signal.rs | 31 +++++++- lib/wasix/src/os/task/task_join_handle.rs | 42 ++++++++++ lib/wasix/src/runners/wasi.rs | 3 + 9 files changed, 181 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 527d024cae1..94fa9670e81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -217,6 +217,16 @@ dependencies = [ "tokio 1.35.1", ] +[[package]] +name = "async-ctrlc" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907279f6e91a51c8ec7cac24711e8308f21da7c10c7700ca2f7e125694ed2df1" +dependencies = [ + "ctrlc", + "futures-core", +] + [[package]] name = "async-trait" version = "0.1.77" @@ -1076,6 +1086,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ctrlc" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b" +dependencies = [ + "nix 0.27.1", + "windows-sys 0.52.0", +] + [[package]] name = "cty" version = "0.2.2" @@ -3056,6 +3076,17 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.1", + "cfg-if 1.0.0", + "libc", +] + [[package]] name = "nom" version = "5.1.3" @@ -6877,6 +6908,7 @@ name = "wasmer-wasix" version = "0.18.0" dependencies = [ "anyhow", + "async-ctrlc", "async-trait", "base64 0.21.5", "bincode", diff --git a/lib/journal/src/concrete/archived.rs b/lib/journal/src/concrete/archived.rs index 6a800480389..65fd0ed0ae5 100644 --- a/lib/journal/src/concrete/archived.rs +++ b/lib/journal/src/concrete/archived.rs @@ -1571,6 +1571,7 @@ pub enum JournalSnapshotTriggerV1 { Listen, Environ, Stdin, + CtrlC, Timer, Sigint, Sigalrm, diff --git a/lib/journal/src/concrete/archived_from.rs b/lib/journal/src/concrete/archived_from.rs index 4bd54513d0b..ac3d99ac24e 100644 --- a/lib/journal/src/concrete/archived_from.rs +++ b/lib/journal/src/concrete/archived_from.rs @@ -177,6 +177,7 @@ impl From for JournalSnapshotTriggerV1 { SnapshotTrigger::FirstListen => JournalSnapshotTriggerV1::Listen, SnapshotTrigger::FirstEnviron => JournalSnapshotTriggerV1::Environ, SnapshotTrigger::FirstStdin => JournalSnapshotTriggerV1::Stdin, + SnapshotTrigger::CtrlC => JournalSnapshotTriggerV1::CtrlC, SnapshotTrigger::PeriodicInterval => JournalSnapshotTriggerV1::Timer, SnapshotTrigger::Sigint => JournalSnapshotTriggerV1::Sigint, SnapshotTrigger::Sigalrm => JournalSnapshotTriggerV1::Sigalrm, @@ -193,6 +194,7 @@ impl From for SnapshotTrigger { JournalSnapshotTriggerV1::Idle => SnapshotTrigger::Idle, JournalSnapshotTriggerV1::Listen => SnapshotTrigger::FirstListen, JournalSnapshotTriggerV1::Environ => SnapshotTrigger::FirstEnviron, + JournalSnapshotTriggerV1::CtrlC => SnapshotTrigger::CtrlC, JournalSnapshotTriggerV1::Stdin => SnapshotTrigger::FirstStdin, JournalSnapshotTriggerV1::Timer => SnapshotTrigger::PeriodicInterval, JournalSnapshotTriggerV1::Sigint => SnapshotTrigger::Sigint, @@ -210,6 +212,7 @@ impl From<&'_ ArchivedJournalSnapshotTriggerV1> for SnapshotTrigger { ArchivedJournalSnapshotTriggerV1::Idle => SnapshotTrigger::Idle, ArchivedJournalSnapshotTriggerV1::Listen => SnapshotTrigger::FirstListen, ArchivedJournalSnapshotTriggerV1::Environ => SnapshotTrigger::FirstEnviron, + ArchivedJournalSnapshotTriggerV1::CtrlC => SnapshotTrigger::CtrlC, ArchivedJournalSnapshotTriggerV1::Stdin => SnapshotTrigger::FirstStdin, ArchivedJournalSnapshotTriggerV1::Timer => SnapshotTrigger::PeriodicInterval, ArchivedJournalSnapshotTriggerV1::Sigint => SnapshotTrigger::Sigint, diff --git a/lib/journal/src/snapshot.rs b/lib/journal/src/snapshot.rs index 15afcb6ae92..4600c23476c 100644 --- a/lib/journal/src/snapshot.rs +++ b/lib/journal/src/snapshot.rs @@ -14,6 +14,8 @@ pub enum SnapshotTrigger { FirstStdin, /// Triggered periodically based on a interval (default 10 seconds) which can be specified using the `snapshot-interval` option PeriodicInterval, + /// Triggered when the CLI presses the CTRL-C key + CtrlC, /// Issued if the user sends an interrupt signal (Ctrl + C). Sigint, /// Alarm clock signal (used for timers) @@ -30,16 +32,17 @@ impl SnapshotTrigger { pub fn only_once(&self) -> bool { matches!( self, - Self::FirstListen | Self::FirstEnviron | Self::FirstStdin + Self::FirstListen | Self::FirstEnviron | Self::FirstStdin | Self::CtrlC ) } } -pub const DEFAULT_SNAPSHOT_TRIGGERS: [SnapshotTrigger; 4] = [ +pub const DEFAULT_SNAPSHOT_TRIGGERS: [SnapshotTrigger; 5] = [ SnapshotTrigger::Idle, SnapshotTrigger::FirstEnviron, SnapshotTrigger::FirstListen, SnapshotTrigger::FirstStdin, + SnapshotTrigger::CtrlC, ]; impl FromStr for SnapshotTrigger { @@ -53,7 +56,8 @@ impl FromStr for SnapshotTrigger { "first-stdin" => Self::FirstStdin, "first-environ" => Self::FirstEnviron, "periodic-interval" => Self::PeriodicInterval, - "intr" | "sigint" | "ctrlc" | "ctrl-c" => Self::Sigint, + "intr" | "sigint" => Self::Sigint, + "ctrlc" | "ctrl-c" => Self::CtrlC, "alarm" | "timer" | "sigalrm" => Self::Sigalrm, "sigtstp" | "ctrlz" | "ctrl-z" => Self::Sigtstp, "stop" | "sigstop" => Self::Sigstop, diff --git a/lib/wasix/Cargo.toml b/lib/wasix/Cargo.toml index aecd7ee57b6..352a5bc0f5a 100644 --- a/lib/wasix/Cargo.toml +++ b/lib/wasix/Cargo.toml @@ -49,6 +49,7 @@ tokio = { version = "1", features = [ ], default_features = false } tokio-stream = { version = "0.1", features = [ "sync" ] } futures = { version = "0.3" } +async-ctrlc = { version = "1.2.0", features = [ "stream" ], optional = true } # used by feature='os' async-trait = { version = "^0.1" } urlencoding = { version = "^2" } @@ -141,6 +142,7 @@ wasmer = { path = "../api", version = "=4.2.5", default-features = false, featur default = ["sys-default"] time = ["tokio/time"] +ctrlc = ["dep:async-ctrlc"] webc_runner_rt_wcgi = ["hyper", "wcgi", "wcgi-host", "tower", "tower-http"] webc_runner_rt_dcgi = ["webc_runner_rt_wcgi", "journal"] @@ -158,6 +160,7 @@ sys-default = [ "host-vnet", "host-threads", "host-reqwest", + "ctrlc" ] sys-poll = [] sys-thread = ["tokio/rt", "tokio/time", "tokio/rt-multi-thread", "rusty_pool"] diff --git a/lib/wasix/src/os/task/process.rs b/lib/wasix/src/os/task/process.rs index d1ba420c8b8..f5a3fe94bbe 100644 --- a/lib/wasix/src/os/task/process.rs +++ b/lib/wasix/src/os/task/process.rs @@ -33,6 +33,7 @@ use super::{ signal::{SignalDeliveryError, SignalHandlerAbi}, task_join_handle::OwnedTaskStatus, thread::WasiMemoryLayout, + TaskStatus, }; /// Represents the ID of a sub-process @@ -147,6 +148,8 @@ impl Into> for MemorySnapshotRegion { pub struct WasiProcessInner { /// Unique ID of this process pub pid: WasiProcessId, + /// Number of threads waiting for children to exit + pub(crate) waiting: Arc, /// The threads that make up this process pub threads: HashMap, /// Number of threads running for this process @@ -331,26 +334,46 @@ impl Drop for WasiProcessWait { impl WasiProcess { pub fn new(pid: WasiProcessId, module_hash: ModuleHash, plane: WasiControlPlaneHandle) -> Self { + let waiting = Arc::new(AtomicU32::new(0)); + let inner = Arc::new(( + Mutex::new(WasiProcessInner { + pid, + threads: Default::default(), + thread_count: Default::default(), + signal_intervals: Default::default(), + children: Default::default(), + checkpoint: WasiProcessCheckpoint::Execute, + wakers: Default::default(), + waiting: waiting.clone(), + snapshot_memory_hash: Default::default(), + }), + Condvar::new(), + )); + + #[derive(Debug)] + struct SignalHandler(LockableWasiProcessInner); + impl SignalHandlerAbi for SignalHandler { + fn signal(&self, signal: u8) -> Result<(), SignalDeliveryError> { + if let Ok(signal) = signal.try_into() { + signal_process_internal(&self.0, signal); + Ok(()) + } else { + Err(SignalDeliveryError) + } + } + } + WasiProcess { pid, module_hash, parent: None, compute: plane, - inner: Arc::new(( - Mutex::new(WasiProcessInner { - pid, - threads: Default::default(), - thread_count: Default::default(), - signal_intervals: Default::default(), - children: Default::default(), - checkpoint: WasiProcessCheckpoint::Execute, - wakers: Default::default(), - snapshot_memory_hash: Default::default(), - }), - Condvar::new(), - )), - finished: Arc::new(OwnedTaskStatus::default()), - waiting: Arc::new(AtomicU32::new(0)), + inner: inner.clone(), + finished: Arc::new( + OwnedTaskStatus::new(TaskStatus::Pending) + .with_signal_handler(Arc::new(SignalHandler(inner))), + ), + waiting, } } @@ -471,26 +494,7 @@ impl WasiProcess { /// Signals all the threads in this process pub fn signal_process(&self, signal: Signal) { - let pid = self.pid(); - tracing::trace!(%pid, "signal-process({:?})", signal); - - { - let inner = self.inner.0.lock().unwrap(); - if self.waiting.load(Ordering::Acquire) > 0 { - let mut triggered = false; - for child in inner.children.iter() { - child.signal_process(signal); - triggered = true; - } - if triggered { - return; - } - } - } - let inner = self.inner.0.lock().unwrap(); - for thread in inner.threads.values() { - thread.signal(signal); - } + signal_process_internal(&self.inner, signal); } /// Signals one of the threads every interval @@ -606,6 +610,27 @@ impl WasiProcess { } } +/// Signals all the threads in this process +fn signal_process_internal(process: &LockableWasiProcessInner, signal: Signal) { + let inner = process.0.lock().unwrap(); + let pid = inner.pid; + tracing::trace!(%pid, "signal-process({:?})", signal); + + if inner.waiting.load(Ordering::Acquire) > 0 { + let mut triggered = false; + for child in inner.children.iter() { + child.signal_process(signal); + triggered = true; + } + if triggered { + return; + } + } + for thread in inner.threads.values() { + thread.signal(signal); + } +} + impl SignalHandlerAbi for WasiProcess { fn signal(&self, sig: u8) -> Result<(), SignalDeliveryError> { if let Ok(sig) = sig.try_into() { diff --git a/lib/wasix/src/os/task/signal.rs b/lib/wasix/src/os/task/signal.rs index 28462829bab..d6b729b888f 100644 --- a/lib/wasix/src/os/task/signal.rs +++ b/lib/wasix/src/os/task/signal.rs @@ -1,4 +1,4 @@ -use std::time::Duration; +use std::{sync::Arc, time::Duration}; use wasmer_wasix_types::types::Signal; @@ -15,6 +15,8 @@ where fn signal(&self, signal: u8) -> Result<(), SignalDeliveryError>; } +pub type DynSignalHandlerAbi = dyn SignalHandlerAbi + Send + Sync + 'static; + #[derive(Debug)] pub struct WasiSignalInterval { /// Signal that will be raised @@ -26,3 +28,30 @@ pub struct WasiSignalInterval { /// Last time that a signal was triggered pub last_signal: u128, } + +pub fn default_signal_handler() -> Arc { + #[derive(Debug)] + struct DefaultHandler {} + impl SignalHandlerAbi for DefaultHandler { + fn signal(&self, signal: u8) -> Result<(), SignalDeliveryError> { + if let Ok(signal) = TryInto::::try_into(signal) { + match signal { + Signal::Sigkill + | Signal::Sigterm + | Signal::Sigabrt + | Signal::Sigquit + | Signal::Sigint + | Signal::Sigstop => { + tracing::debug!("handling terminate signal"); + std::process::exit(1); + } + signal => tracing::info!("unhandled signal - {:?}", signal), + } + } else { + tracing::info!("unknown signal - {}", signal) + } + Ok(()) + } + } + Arc::new(DefaultHandler {}) +} diff --git a/lib/wasix/src/os/task/task_join_handle.rs b/lib/wasix/src/os/task/task_join_handle.rs index 18d7271bac6..a9b11cfa5e9 100644 --- a/lib/wasix/src/os/task/task_join_handle.rs +++ b/lib/wasix/src/os/task/task_join_handle.rs @@ -8,6 +8,8 @@ use wasmer_wasix_types::wasi::{Errno, ExitCode}; use crate::WasiRuntimeError; +use super::signal::{default_signal_handler, DynSignalHandlerAbi}; + #[derive(Clone, Debug)] pub enum TaskStatus { Pending, @@ -70,6 +72,9 @@ pub trait VirtualTaskHandle: std::fmt::Debug + Send + Sync + 'static { /// A handle that allows awaiting the termination of a task, and retrieving its exit code. #[derive(Debug)] pub struct OwnedTaskStatus { + // The signal handler that can be invoked for this owned task + signal_handler: Arc, + watch_tx: tokio::sync::watch::Sender, // Even through unused, without this receive there is a race condition // where the previously sent values are lost. @@ -81,11 +86,23 @@ impl OwnedTaskStatus { pub fn new(status: TaskStatus) -> Self { let (tx, rx) = tokio::sync::watch::channel(status); Self { + signal_handler: default_signal_handler(), watch_tx: tx, watch_rx: rx, } } + /// Sets the signal handler used for this owned task + pub fn set_signal_handler(&mut self, handler: Arc) { + self.signal_handler = handler; + } + + /// Attaches a signal handler + pub fn with_signal_handler(mut self, handler: Arc) -> Self { + self.set_signal_handler(handler); + self + } + pub fn new_finished_with_code(code: ExitCode) -> Self { Self::new(TaskStatus::Finished(Ok(code))) } @@ -144,6 +161,7 @@ impl OwnedTaskStatus { pub fn handle(&self) -> TaskJoinHandle { TaskJoinHandle { + signal_handler: self.signal_handler.clone(), watch: self.watch_tx.subscribe(), } } @@ -158,6 +176,7 @@ impl Default for OwnedTaskStatus { /// A handle that allows awaiting the termination of a task, and retrieving its exit code. #[derive(Clone, Debug)] pub struct TaskJoinHandle { + signal_handler: Arc, watch: tokio::sync::watch::Receiver, } @@ -167,6 +186,29 @@ impl TaskJoinHandle { self.watch.borrow().clone() } + #[cfg(feature = "ctrlc")] + pub fn install_ctrlc_handler(&self) { + use wasmer::FromToNativeWasmType; + use wasmer_wasix_types::wasi::Signal; + + let signal_handler = self.signal_handler.clone(); + + let mut ctrlc = async_ctrlc::CtrlC::new().expect("cannot create Ctrl+C handler"); + let task_handle = self.clone(); + tokio::spawn(async move { + // Loop sending ctrl-c presses as signals to the signal handler + loop { + let ctrlc_pin = &mut ctrlc; + ctrlc_pin.await; + + if let Err(err) = signal_handler.signal(Signal::Sigint.to_native() as u8) { + tracing::error!("failed to process signal - {}", err); + std::process::exit(1); + } + } + }); + } + /// Wait until the task finishes. pub async fn wait_finished(&mut self) -> Result> { loop { diff --git a/lib/wasix/src/runners/wasi.rs b/lib/wasix/src/runners/wasi.rs index fb7fcb5d2e6..c36b95cbb65 100644 --- a/lib/wasix/src/runners/wasi.rs +++ b/lib/wasix/src/runners/wasi.rs @@ -326,6 +326,9 @@ impl crate::runners::Runner for WasiRunner { .await .context("Spawn failed")?; + #[cfg(feature = "ctrlc")] + task_handle.install_ctrlc_handler(); + task_handle .wait_finished() .await From bb430fe4786f2ba6a0e0cfa95912eeadfb2a198e Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 01:16:10 +1100 Subject: [PATCH 45/67] Now taking snapshots on signal events however its not waking properly yet --- lib/journal/src/concrete/archived.rs | 1 - lib/journal/src/concrete/archived_from.rs | 3 --- lib/journal/src/snapshot.rs | 10 +++------ lib/wasix/src/os/task/process.rs | 26 ++++++++++++++++++++++- lib/wasix/src/state/env.rs | 8 +++++++ 5 files changed, 36 insertions(+), 12 deletions(-) diff --git a/lib/journal/src/concrete/archived.rs b/lib/journal/src/concrete/archived.rs index 65fd0ed0ae5..6a800480389 100644 --- a/lib/journal/src/concrete/archived.rs +++ b/lib/journal/src/concrete/archived.rs @@ -1571,7 +1571,6 @@ pub enum JournalSnapshotTriggerV1 { Listen, Environ, Stdin, - CtrlC, Timer, Sigint, Sigalrm, diff --git a/lib/journal/src/concrete/archived_from.rs b/lib/journal/src/concrete/archived_from.rs index ac3d99ac24e..4bd54513d0b 100644 --- a/lib/journal/src/concrete/archived_from.rs +++ b/lib/journal/src/concrete/archived_from.rs @@ -177,7 +177,6 @@ impl From for JournalSnapshotTriggerV1 { SnapshotTrigger::FirstListen => JournalSnapshotTriggerV1::Listen, SnapshotTrigger::FirstEnviron => JournalSnapshotTriggerV1::Environ, SnapshotTrigger::FirstStdin => JournalSnapshotTriggerV1::Stdin, - SnapshotTrigger::CtrlC => JournalSnapshotTriggerV1::CtrlC, SnapshotTrigger::PeriodicInterval => JournalSnapshotTriggerV1::Timer, SnapshotTrigger::Sigint => JournalSnapshotTriggerV1::Sigint, SnapshotTrigger::Sigalrm => JournalSnapshotTriggerV1::Sigalrm, @@ -194,7 +193,6 @@ impl From for SnapshotTrigger { JournalSnapshotTriggerV1::Idle => SnapshotTrigger::Idle, JournalSnapshotTriggerV1::Listen => SnapshotTrigger::FirstListen, JournalSnapshotTriggerV1::Environ => SnapshotTrigger::FirstEnviron, - JournalSnapshotTriggerV1::CtrlC => SnapshotTrigger::CtrlC, JournalSnapshotTriggerV1::Stdin => SnapshotTrigger::FirstStdin, JournalSnapshotTriggerV1::Timer => SnapshotTrigger::PeriodicInterval, JournalSnapshotTriggerV1::Sigint => SnapshotTrigger::Sigint, @@ -212,7 +210,6 @@ impl From<&'_ ArchivedJournalSnapshotTriggerV1> for SnapshotTrigger { ArchivedJournalSnapshotTriggerV1::Idle => SnapshotTrigger::Idle, ArchivedJournalSnapshotTriggerV1::Listen => SnapshotTrigger::FirstListen, ArchivedJournalSnapshotTriggerV1::Environ => SnapshotTrigger::FirstEnviron, - ArchivedJournalSnapshotTriggerV1::CtrlC => SnapshotTrigger::CtrlC, ArchivedJournalSnapshotTriggerV1::Stdin => SnapshotTrigger::FirstStdin, ArchivedJournalSnapshotTriggerV1::Timer => SnapshotTrigger::PeriodicInterval, ArchivedJournalSnapshotTriggerV1::Sigint => SnapshotTrigger::Sigint, diff --git a/lib/journal/src/snapshot.rs b/lib/journal/src/snapshot.rs index 4600c23476c..15afcb6ae92 100644 --- a/lib/journal/src/snapshot.rs +++ b/lib/journal/src/snapshot.rs @@ -14,8 +14,6 @@ pub enum SnapshotTrigger { FirstStdin, /// Triggered periodically based on a interval (default 10 seconds) which can be specified using the `snapshot-interval` option PeriodicInterval, - /// Triggered when the CLI presses the CTRL-C key - CtrlC, /// Issued if the user sends an interrupt signal (Ctrl + C). Sigint, /// Alarm clock signal (used for timers) @@ -32,17 +30,16 @@ impl SnapshotTrigger { pub fn only_once(&self) -> bool { matches!( self, - Self::FirstListen | Self::FirstEnviron | Self::FirstStdin | Self::CtrlC + Self::FirstListen | Self::FirstEnviron | Self::FirstStdin ) } } -pub const DEFAULT_SNAPSHOT_TRIGGERS: [SnapshotTrigger; 5] = [ +pub const DEFAULT_SNAPSHOT_TRIGGERS: [SnapshotTrigger; 4] = [ SnapshotTrigger::Idle, SnapshotTrigger::FirstEnviron, SnapshotTrigger::FirstListen, SnapshotTrigger::FirstStdin, - SnapshotTrigger::CtrlC, ]; impl FromStr for SnapshotTrigger { @@ -56,8 +53,7 @@ impl FromStr for SnapshotTrigger { "first-stdin" => Self::FirstStdin, "first-environ" => Self::FirstEnviron, "periodic-interval" => Self::PeriodicInterval, - "intr" | "sigint" => Self::Sigint, - "ctrlc" | "ctrl-c" => Self::CtrlC, + "intr" | "sigint" | "ctrlc" | "ctrl-c" => Self::Sigint, "alarm" | "timer" | "sigalrm" => Self::Sigalrm, "sigtstp" | "ctrlz" | "ctrl-z" => Self::Sigtstp, "stop" | "sigstop" => Self::Sigstop, diff --git a/lib/wasix/src/os/task/process.rs b/lib/wasix/src/os/task/process.rs index f5a3fe94bbe..e386c9bca45 100644 --- a/lib/wasix/src/os/task/process.rs +++ b/lib/wasix/src/os/task/process.rs @@ -161,6 +161,9 @@ pub struct WasiProcessInner { /// Represents a checkpoint which blocks all the threads /// and then executes some maintenance action pub checkpoint: WasiProcessCheckpoint, + /// Flag that indicates that the process should snapshot + /// itself and exit when the ctrl-c key is pressed + pub snapshot_on_sigint: bool, /// Any wakers waiting on this process (for example for a checkpoint) pub wakers: Vec, /// The snapshot memory significantly reduce the amount of @@ -345,6 +348,7 @@ impl WasiProcess { checkpoint: WasiProcessCheckpoint::Execute, wakers: Default::default(), waiting: waiting.clone(), + snapshot_on_sigint: false, snapshot_memory_hash: Default::default(), }), Condvar::new(), @@ -612,10 +616,28 @@ impl WasiProcess { /// Signals all the threads in this process fn signal_process_internal(process: &LockableWasiProcessInner, signal: Signal) { - let inner = process.0.lock().unwrap(); + let mut inner = process.0.lock().unwrap(); let pid = inner.pid; tracing::trace!(%pid, "signal-process({:?})", signal); + // If the snapshot on ctrl-c is currently registered then we need + // to take a snapshot and exit + if inner.snapshot_on_sigint { + tracing::debug!(%pid, "snapshot-on-interrupt-signal"); + + // Initiate the checksum + inner.checkpoint = WasiProcessCheckpoint::Snapshot { + trigger: SnapshotTrigger::Sigint, + }; + for waker in inner.wakers.drain(..) { + waker.wake(); + } + process.1.notify_all(); + return; + } + + // Check if there are subprocesses that will receive this signal + // instead of this process if inner.waiting.load(Ordering::Acquire) > 0 { let mut triggered = false; for child in inner.children.iter() { @@ -626,6 +648,8 @@ fn signal_process_internal(process: &LockableWasiProcessInner, signal: Signal) { return; } } + + // Otherwise just send the signal to all the threads for thread in inner.threads.values() { thread.signal(signal); } diff --git a/lib/wasix/src/state/env.rs b/lib/wasix/src/state/env.rs index c9a0fa9e267..cc90621e2e3 100644 --- a/lib/wasix/src/state/env.rs +++ b/lib/wasix/src/state/env.rs @@ -500,6 +500,14 @@ impl WasiEnv { init.control_plane.new_process(module_hash)? }; + if init + .snapshot_on + .iter() + .any(|s| matches!(s, SnapshotTrigger::Sigint)) + { + process.inner.0.lock().unwrap().snapshot_on_sigint = true; + } + let layout = WasiMemoryLayout::default(); let thread = if let Some(t) = init.thread { t From 68fedb8704e9da1684eeb841abef3a6d28e4563e Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 09:20:59 +1100 Subject: [PATCH 46/67] Fixed an issue where ctrl-c was not triggering during deep threading and properly implemented the snapshot on sigint issue --- Cargo.lock | 42 ++------- lib/journal/src/concrete/archived_from.rs | 1 + lib/journal/src/snapshot.rs | 5 +- lib/wasix/Cargo.toml | 3 +- lib/wasix/src/lib.rs | 2 +- lib/wasix/src/os/task/mod.rs | 1 + lib/wasix/src/os/task/process.rs | 93 +++++++++++++++---- lib/wasix/src/os/task/task_join_handle.rs | 8 +- lib/wasix/src/os/task/thread.rs | 21 +++++ lib/wasix/src/runtime/task_manager/tokio.rs | 34 ++++++- lib/wasix/src/state/env.rs | 28 ++---- .../journal/do_checkpoint_from_outside.rs | 24 +++++ lib/wasix/src/syscalls/journal/mod.rs | 7 +- .../src/syscalls/journal/wait_for_snapshot.rs | 4 +- lib/wasix/src/syscalls/mod.rs | 6 +- 15 files changed, 184 insertions(+), 95 deletions(-) create mode 100644 lib/wasix/src/syscalls/journal/do_checkpoint_from_outside.rs diff --git a/Cargo.lock b/Cargo.lock index 94fa9670e81..d90fda3034d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -217,16 +217,6 @@ dependencies = [ "tokio 1.35.1", ] -[[package]] -name = "async-ctrlc" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907279f6e91a51c8ec7cac24711e8308f21da7c10c7700ca2f7e125694ed2df1" -dependencies = [ - "ctrlc", - "futures-core", -] - [[package]] name = "async-trait" version = "0.1.77" @@ -1086,16 +1076,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "ctrlc" -version = "3.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b" -dependencies = [ - "nix 0.27.1", - "windows-sys 0.52.0", -] - [[package]] name = "cty" version = "0.2.2" @@ -3076,17 +3056,6 @@ dependencies = [ "pin-utils", ] -[[package]] -name = "nix" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags 2.4.1", - "cfg-if 1.0.0", - "libc", -] - [[package]] name = "nom" version = "5.1.3" @@ -4644,6 +4613,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "simdutf8" version = "0.1.4" @@ -5127,6 +5105,7 @@ dependencies = [ "mio 0.8.10", "num_cpus", "pin-project-lite", + "signal-hook-registry", "socket2 0.5.5", "tokio-macros", "windows-sys 0.48.0", @@ -6908,7 +6887,6 @@ name = "wasmer-wasix" version = "0.18.0" dependencies = [ "anyhow", - "async-ctrlc", "async-trait", "base64 0.21.5", "bincode", diff --git a/lib/journal/src/concrete/archived_from.rs b/lib/journal/src/concrete/archived_from.rs index 4bd54513d0b..1e76643a54f 100644 --- a/lib/journal/src/concrete/archived_from.rs +++ b/lib/journal/src/concrete/archived_from.rs @@ -177,6 +177,7 @@ impl From for JournalSnapshotTriggerV1 { SnapshotTrigger::FirstListen => JournalSnapshotTriggerV1::Listen, SnapshotTrigger::FirstEnviron => JournalSnapshotTriggerV1::Environ, SnapshotTrigger::FirstStdin => JournalSnapshotTriggerV1::Stdin, + SnapshotTrigger::FirstSigint => JournalSnapshotTriggerV1::Sigint, SnapshotTrigger::PeriodicInterval => JournalSnapshotTriggerV1::Timer, SnapshotTrigger::Sigint => JournalSnapshotTriggerV1::Sigint, SnapshotTrigger::Sigalrm => JournalSnapshotTriggerV1::Sigalrm, diff --git a/lib/journal/src/snapshot.rs b/lib/journal/src/snapshot.rs index 15afcb6ae92..ce5b7029028 100644 --- a/lib/journal/src/snapshot.rs +++ b/lib/journal/src/snapshot.rs @@ -12,6 +12,8 @@ pub enum SnapshotTrigger { FirstEnviron, /// Triggered when the process reads stdin for the first time FirstStdin, + /// Issued on the first interrupt signal (Ctrl + C) the process receives, after that normal CTRL-C will apply. + FirstSigint, /// Triggered periodically based on a interval (default 10 seconds) which can be specified using the `snapshot-interval` option PeriodicInterval, /// Issued if the user sends an interrupt signal (Ctrl + C). @@ -30,7 +32,7 @@ impl SnapshotTrigger { pub fn only_once(&self) -> bool { matches!( self, - Self::FirstListen | Self::FirstEnviron | Self::FirstStdin + Self::FirstListen | Self::FirstEnviron | Self::FirstStdin | Self::FirstSigint ) } } @@ -52,6 +54,7 @@ impl FromStr for SnapshotTrigger { "first-listen" => Self::FirstListen, "first-stdin" => Self::FirstStdin, "first-environ" => Self::FirstEnviron, + "first-intr" | "first-sigint" | "first-ctrlc" | "first-ctrl-c" => Self::FirstSigint, "periodic-interval" => Self::PeriodicInterval, "intr" | "sigint" | "ctrlc" | "ctrl-c" => Self::Sigint, "alarm" | "timer" | "sigalrm" => Self::Sigalrm, diff --git a/lib/wasix/Cargo.toml b/lib/wasix/Cargo.toml index 352a5bc0f5a..b7f9423da54 100644 --- a/lib/wasix/Cargo.toml +++ b/lib/wasix/Cargo.toml @@ -49,7 +49,6 @@ tokio = { version = "1", features = [ ], default_features = false } tokio-stream = { version = "0.1", features = [ "sync" ] } futures = { version = "0.3" } -async-ctrlc = { version = "1.2.0", features = [ "stream" ], optional = true } # used by feature='os' async-trait = { version = "^0.1" } urlencoding = { version = "^2" } @@ -142,7 +141,7 @@ wasmer = { path = "../api", version = "=4.2.5", default-features = false, featur default = ["sys-default"] time = ["tokio/time"] -ctrlc = ["dep:async-ctrlc"] +ctrlc = ["tokio/signal"] webc_runner_rt_wcgi = ["hyper", "wcgi", "wcgi-host", "tower", "tower-http"] webc_runner_rt_dcgi = ["webc_runner_rt_wcgi", "journal"] diff --git a/lib/wasix/src/lib.rs b/lib/wasix/src/lib.rs index b055fd9deae..a5335068804 100644 --- a/lib/wasix/src/lib.rs +++ b/lib/wasix/src/lib.rs @@ -102,7 +102,7 @@ pub use crate::{ WasiEnv, WasiEnvBuilder, WasiEnvInit, WasiFunctionEnv, WasiInstanceHandles, WasiStateCreationError, ALL_RIGHTS, }, - syscalls::{rewind, rewind_ext, types, unwind}, + syscalls::{journal::wait_for_snapshot, rewind, rewind_ext, types, unwind}, utils::is_wasix_module, utils::{ get_wasi_version, get_wasi_versions, is_wasi_module, diff --git a/lib/wasix/src/os/task/mod.rs b/lib/wasix/src/os/task/mod.rs index 95fe609c93f..5839cb9442f 100644 --- a/lib/wasix/src/os/task/mod.rs +++ b/lib/wasix/src/os/task/mod.rs @@ -6,6 +6,7 @@ pub mod signal; mod task_join_handle; pub mod thread; +pub(crate) use process::WasiProcessInner; pub use task_join_handle::{ OwnedTaskStatus, TaskJoinHandle, TaskStatus, TaskTerminatedError, VirtualTaskHandle, }; diff --git a/lib/wasix/src/os/task/process.rs b/lib/wasix/src/os/task/process.rs index e386c9bca45..fcbca171af7 100644 --- a/lib/wasix/src/os/task/process.rs +++ b/lib/wasix/src/os/task/process.rs @@ -1,9 +1,12 @@ #[cfg(feature = "journal")] use crate::{journal::JournalEffector, unwind, WasiResult}; use crate::{ - journal::SnapshotTrigger, runtime::module_cache::ModuleHash, WasiEnv, WasiRuntimeError, + journal::SnapshotTrigger, runtime::module_cache::ModuleHash, + syscalls::do_checkpoint_from_outside, WasiEnv, WasiRuntimeError, }; use serde::{Deserialize, Serialize}; +#[cfg(feature = "journal")] +use std::collections::HashSet; use std::{ collections::HashMap, convert::TryInto, @@ -161,9 +164,9 @@ pub struct WasiProcessInner { /// Represents a checkpoint which blocks all the threads /// and then executes some maintenance action pub checkpoint: WasiProcessCheckpoint, - /// Flag that indicates that the process should snapshot - /// itself and exit when the ctrl-c key is pressed - pub snapshot_on_sigint: bool, + /// List of situations that the process will checkpoint on + #[cfg(feature = "journal")] + pub snapshot_on: HashSet, /// Any wakers waiting on this process (for example for a checkpoint) pub wakers: Vec, /// The snapshot memory significantly reduce the amount of @@ -313,6 +316,53 @@ impl WasiProcessInner { Ok(Ok(MaybeCheckpointResult::Unwinding)) } + + // Execute any checkpoints that can be executed while outside of the WASM process + #[cfg(not(feature = "journal"))] + pub fn do_checkpoints_from_outside<'a>(_ctx: &mut FunctionEnvMut<'a, WasiEnv>) {} + + // Execute any checkpoints that can be executed while outside of the WASM process + #[cfg(feature = "journal")] + pub fn do_checkpoints_from_outside<'a>(mut ctx: &mut FunctionEnvMut<'a, WasiEnv>) { + let inner = ctx.data().process.inner.clone(); + let mut guard = inner.0.lock().unwrap(); + + // Wait for the checkpoint to finish (or if we are the last thread + // to freeze then we have to execute the checksum operation) + while let WasiProcessCheckpoint::Snapshot { trigger } = guard.checkpoint { + ctx.data().thread.set_checkpointing(true); + + // Now if we are the last thread we also write the memory + let is_last_thread = guard + .threads + .values() + .all(|t| t.is_check_pointing() || t.is_deep_sleeping()); + if is_last_thread { + if let Err(err) = + JournalEffector::save_memory_and_snapshot(&mut ctx, &mut guard, trigger) + { + inner.1.notify_all(); + tracing::error!("failed to snapshot memory and threads - {}", err); + return; + } + + // Clear the checkpointing flag and notify everyone to wake up + ctx.data().thread.set_checkpointing(false); + trace!("checkpoint complete"); + guard.checkpoint = WasiProcessCheckpoint::Execute; + for waker in guard.wakers.drain(..) { + waker.wake(); + } + inner.1.notify_all(); + } else { + guard = inner.1.wait(guard).unwrap(); + } + continue; + } + + ctx.data().thread.set_checkpointing(false); + trace!("checkpoint finished"); + } } // TODO: why do we need this, how is it used? @@ -348,7 +398,7 @@ impl WasiProcess { checkpoint: WasiProcessCheckpoint::Execute, wakers: Default::default(), waiting: waiting.clone(), - snapshot_on_sigint: false, + snapshot_on: Default::default(), snapshot_memory_hash: Default::default(), }), Condvar::new(), @@ -616,31 +666,34 @@ impl WasiProcess { /// Signals all the threads in this process fn signal_process_internal(process: &LockableWasiProcessInner, signal: Signal) { - let mut inner = process.0.lock().unwrap(); - let pid = inner.pid; + let mut guard = process.0.lock().unwrap(); + let pid = guard.pid; tracing::trace!(%pid, "signal-process({:?})", signal); // If the snapshot on ctrl-c is currently registered then we need // to take a snapshot and exit - if inner.snapshot_on_sigint { + if signal == Signal::Sigint + && (guard.snapshot_on.contains(&SnapshotTrigger::Sigint) + || guard.snapshot_on.remove(&SnapshotTrigger::FirstSigint)) + { + drop(guard); + tracing::debug!(%pid, "snapshot-on-interrupt-signal"); - // Initiate the checksum - inner.checkpoint = WasiProcessCheckpoint::Snapshot { - trigger: SnapshotTrigger::Sigint, - }; - for waker in inner.wakers.drain(..) { - waker.wake(); - } - process.1.notify_all(); + do_checkpoint_from_outside( + process, + WasiProcessCheckpoint::Snapshot { + trigger: SnapshotTrigger::Sigint, + }, + ); return; - } + }; // Check if there are subprocesses that will receive this signal // instead of this process - if inner.waiting.load(Ordering::Acquire) > 0 { + if guard.waiting.load(Ordering::Acquire) > 0 { let mut triggered = false; - for child in inner.children.iter() { + for child in guard.children.iter() { child.signal_process(signal); triggered = true; } @@ -650,7 +703,7 @@ fn signal_process_internal(process: &LockableWasiProcessInner, signal: Signal) { } // Otherwise just send the signal to all the threads - for thread in inner.threads.values() { + for thread in guard.threads.values() { thread.signal(signal); } } diff --git a/lib/wasix/src/os/task/task_join_handle.rs b/lib/wasix/src/os/task/task_join_handle.rs index a9b11cfa5e9..5c2329e7f42 100644 --- a/lib/wasix/src/os/task/task_join_handle.rs +++ b/lib/wasix/src/os/task/task_join_handle.rs @@ -176,6 +176,7 @@ impl Default for OwnedTaskStatus { /// A handle that allows awaiting the termination of a task, and retrieving its exit code. #[derive(Clone, Debug)] pub struct TaskJoinHandle { + #[allow(unused)] signal_handler: Arc, watch: tokio::sync::watch::Receiver, } @@ -193,14 +194,9 @@ impl TaskJoinHandle { let signal_handler = self.signal_handler.clone(); - let mut ctrlc = async_ctrlc::CtrlC::new().expect("cannot create Ctrl+C handler"); - let task_handle = self.clone(); tokio::spawn(async move { // Loop sending ctrl-c presses as signals to the signal handler - loop { - let ctrlc_pin = &mut ctrlc; - ctrlc_pin.await; - + while tokio::signal::ctrl_c().await.is_ok() { if let Err(err) = signal_handler.signal(Signal::Sigint.to_native() as u8) { tracing::error!("failed to process signal - {}", err); std::process::exit(1); diff --git a/lib/wasix/src/os/task/thread.rs b/lib/wasix/src/os/task/thread.rs index 6ff3a988ff7..45fefc8234c 100644 --- a/lib/wasix/src/os/task/thread.rs +++ b/lib/wasix/src/os/task/thread.rs @@ -360,6 +360,27 @@ impl WasiThread { false } + /// Waits for a signal to arrive + pub async fn wait_for_signal(&self) { + // This poller will process any signals when the main working function is idle + struct SignalPoller<'a> { + thread: &'a WasiThread, + } + impl<'a> std::future::Future for SignalPoller<'a> { + type Output = (); + fn poll( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + if self.thread.has_signals_or_subscribe(cx.waker()) { + return std::task::Poll::Ready(()); + } + std::task::Poll::Pending + } + } + SignalPoller { thread: self }.await + } + /// Returns all the signals that are waiting to be processed pub fn pop_signals_or_subscribe(&self, waker: &Waker) -> Option> { let mut guard = self.state.signals.lock().unwrap(); diff --git a/lib/wasix/src/runtime/task_manager/tokio.rs b/lib/wasix/src/runtime/task_manager/tokio.rs index deaa7006945..39408896890 100644 --- a/lib/wasix/src/runtime/task_manager/tokio.rs +++ b/lib/wasix/src/runtime/task_manager/tokio.rs @@ -148,7 +148,7 @@ impl VirtualTaskManager for TokioTaskManager { // Create the context on a new store let run = task.run; let recycle = task.recycle; - let (ctx, store) = WasiFunctionEnv::new_with_store( + let (ctx, mut store) = WasiFunctionEnv::new_with_store( task.module, task.env, task.globals, @@ -161,10 +161,38 @@ impl VirtualTaskManager for TokioTaskManager { if let Some(trigger) = task.trigger { tracing::trace!("spawning task_wasm trigger in async pool"); - let trigger = trigger(); + let mut trigger = trigger(); let pool = self.pool.clone(); self.rt.handle().spawn(async move { - let result = trigger.await; + // We wait for either the trigger or for a snapshot to take place + let result = loop { + let env = ctx.data(&store); + break tokio::select! { + r = &mut trigger => r, + _ = env.thread.wait_for_signal() => { + tracing::debug!("wait-for-signal(triggered)"); + let mut ctx = ctx.env.clone().into_mut(&mut store); + if let Err(err) = crate::WasiEnv::process_signals_and_exit(&mut ctx) { + match err { + crate::WasiError::Exit(code) => Err(code), + err => { + tracing::error!("failed to process signals - {}", err); + continue; + } + } + } else { + continue; + } + } + _ = crate::wait_for_snapshot(env) => { + tracing::debug!("wait-for-snapshot(triggered)"); + let mut ctx = ctx.env.clone().into_mut(&mut store); + crate::os::task::WasiProcessInner::do_checkpoints_from_outside(&mut ctx); + continue; + } + }; + }; + // Build the task that will go on the callback pool.execute(move || { // Invoke the callback diff --git a/lib/wasix/src/state/env.rs b/lib/wasix/src/state/env.rs index cc90621e2e3..beb81da734c 100644 --- a/lib/wasix/src/state/env.rs +++ b/lib/wasix/src/state/env.rs @@ -1,5 +1,3 @@ -#[cfg(feature = "journal")] -use std::collections::HashSet; use std::{ collections::HashMap, ops::Deref, @@ -328,10 +326,6 @@ pub struct WasiEnv { /// (this is normally used so that the instance can be reused later on) pub(crate) disable_fs_cleanup: bool, - /// List of situations that the process will checkpoint on - #[cfg(feature = "journal")] - snapshot_on: HashSet, - /// Inner functions and references that are loaded before the environment starts /// (inner is not safe to send between threads and so it is private and will /// not be cloned when `WasiEnv` is cloned) @@ -363,8 +357,6 @@ impl Clone for WasiEnv { enable_deep_sleep: self.enable_deep_sleep, enable_journal: self.enable_journal, replaying_journal: self.replaying_journal, - #[cfg(feature = "journal")] - snapshot_on: self.snapshot_on.clone(), disable_fs_cleanup: self.disable_fs_cleanup, } } @@ -404,8 +396,6 @@ impl WasiEnv { enable_deep_sleep: self.enable_deep_sleep, enable_journal: self.enable_journal, replaying_journal: false, - #[cfg(feature = "journal")] - snapshot_on: self.snapshot_on.clone(), disable_fs_cleanup: self.disable_fs_cleanup, }; Ok((new_env, handle)) @@ -500,12 +490,9 @@ impl WasiEnv { init.control_plane.new_process(module_hash)? }; - if init - .snapshot_on - .iter() - .any(|s| matches!(s, SnapshotTrigger::Sigint)) + #[cfg(feature = "journal")] { - process.inner.0.lock().unwrap().snapshot_on_sigint = true; + process.inner.0.lock().unwrap().snapshot_on = init.snapshot_on.into_iter().collect(); } let layout = WasiMemoryLayout::default(); @@ -534,8 +521,6 @@ impl WasiEnv { runtime: init.runtime, bin_factory: init.bin_factory, capabilities: init.capabilities, - #[cfg(feature = "journal")] - snapshot_on: init.snapshot_on.into_iter().collect(), disable_fs_cleanup: false, }; env.owned_handles.push(thread); @@ -816,6 +801,7 @@ impl WasiEnv { } Ok(true) } else { + tracing::trace!("no signal handler"); Ok(false) } } @@ -973,16 +959,18 @@ impl WasiEnv { /// Returns true if a particular snapshot trigger is enabled #[cfg(feature = "journal")] pub fn has_snapshot_trigger(&self, trigger: SnapshotTrigger) -> bool { - self.snapshot_on.contains(&trigger) + let guard = self.process.inner.0.lock().unwrap(); + guard.snapshot_on.contains(&trigger) } /// Returns true if a particular snapshot trigger is enabled #[cfg(feature = "journal")] pub fn pop_snapshot_trigger(&mut self, trigger: SnapshotTrigger) -> bool { + let mut guard = self.process.inner.0.lock().unwrap(); if trigger.only_once() { - self.snapshot_on.remove(&trigger) + guard.snapshot_on.remove(&trigger) } else { - self.snapshot_on.contains(&trigger) + guard.snapshot_on.contains(&trigger) } } diff --git a/lib/wasix/src/syscalls/journal/do_checkpoint_from_outside.rs b/lib/wasix/src/syscalls/journal/do_checkpoint_from_outside.rs new file mode 100644 index 00000000000..36603d3904d --- /dev/null +++ b/lib/wasix/src/syscalls/journal/do_checkpoint_from_outside.rs @@ -0,0 +1,24 @@ +use crate::os::task::process::LockableWasiProcessInner; + +use super::WasiProcessCheckpoint; + +pub(crate) fn do_checkpoint_from_outside( + process: &LockableWasiProcessInner, + checkpoint: WasiProcessCheckpoint, +) { + let mut guard = process.0.lock().unwrap(); + + // Initiate the checksum (if one already exists we must wait for it to end + // before we start the next checksum) + + // TODO: Disabled as this blocks the async runtime + //while !matches!(guard.checkpoint, WasiProcessCheckpoint::Execute) { + // guard = process.1.wait(guard).unwrap(); + //} + + guard.checkpoint = checkpoint; + for waker in guard.wakers.drain(..) { + waker.wake(); + } + process.1.notify_all(); +} diff --git a/lib/wasix/src/syscalls/journal/mod.rs b/lib/wasix/src/syscalls/journal/mod.rs index 10e4eeaa4f2..4c31455a561 100644 --- a/lib/wasix/src/syscalls/journal/mod.rs +++ b/lib/wasix/src/syscalls/journal/mod.rs @@ -1,5 +1,6 @@ mod actions; mod clear_ethereal; +mod do_checkpoint_from_outside; mod maybe_snapshot; mod maybe_snapshot_many; mod maybe_snapshot_once; @@ -9,13 +10,9 @@ mod wait_for_snapshot; use actions::*; use clear_ethereal::*; -use maybe_snapshot::*; -use maybe_snapshot_many::*; -use maybe_snapshot_once::*; -use restore_snapshot::*; -use wait_for_snapshot::*; use wasmer_journal::JournalEntry; +pub use do_checkpoint_from_outside::*; pub use maybe_snapshot::*; pub use maybe_snapshot_many::*; pub use maybe_snapshot_once::*; diff --git a/lib/wasix/src/syscalls/journal/wait_for_snapshot.rs b/lib/wasix/src/syscalls/journal/wait_for_snapshot.rs index 856df4a21a8..bead2155f7f 100644 --- a/lib/wasix/src/syscalls/journal/wait_for_snapshot.rs +++ b/lib/wasix/src/syscalls/journal/wait_for_snapshot.rs @@ -1,12 +1,12 @@ use super::*; #[cfg(not(feature = "journal"))] -pub fn wait_for_snapshot(_env: &WasiEnv) -> Pin>> { +pub fn wait_for_snapshot(_env: &WasiEnv) -> Pin + Send + Sync>> { Box::pin(std::future::pending()) } #[cfg(feature = "journal")] -pub fn wait_for_snapshot(env: &WasiEnv) -> Pin>> { +pub fn wait_for_snapshot(env: &WasiEnv) -> Pin + Send + Sync>> { use crate::os::task::process::{LockableWasiProcessInner, WasiProcessCheckpoint}; struct Poller { diff --git a/lib/wasix/src/syscalls/mod.rs b/lib/wasix/src/syscalls/mod.rs index b3fd1c23112..4d384e355db 100644 --- a/lib/wasix/src/syscalls/mod.rs +++ b/lib/wasix/src/syscalls/mod.rs @@ -315,14 +315,14 @@ where } // This poller will process any signals when the main working function is idle - struct Poller<'a, 'b, Fut, T> + struct SignalPoller<'a, 'b, Fut, T> where Fut: Future>, { ctx: &'a mut FunctionEnvMut<'b, WasiEnv>, pinned_work: Pin>, } - impl<'a, 'b, Fut, T> Future for Poller<'a, 'b, Fut, T> + impl<'a, 'b, Fut, T> Future for SignalPoller<'a, 'b, Fut, T> where Fut: Future>, { @@ -344,7 +344,7 @@ where // Block on the work let mut pinned_work = Box::pin(work); let tasks = env.tasks().clone(); - let poller = Poller { ctx, pinned_work }; + let poller = SignalPoller { ctx, pinned_work }; block_on_with_timeout(&tasks, timeout, poller) } From 375775488a5dd5baa321f16a7dbc60192c10a0a8 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 09:27:33 +1100 Subject: [PATCH 47/67] Renaming some of the spawn functions to make them easier to understand --- lib/wasix/src/journal/effector/thread_state.rs | 4 ++-- lib/wasix/src/syscalls/wasi/thread_spawn.rs | 2 +- lib/wasix/src/syscalls/wasix/thread_spawn.rs | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/wasix/src/journal/effector/thread_state.rs b/lib/wasix/src/journal/effector/thread_state.rs index 9e969ec2abb..1658aa2268f 100644 --- a/lib/wasix/src/journal/effector/thread_state.rs +++ b/lib/wasix/src/journal/effector/thread_state.rs @@ -4,7 +4,7 @@ use wasmer_wasix_types::wasix::ThreadStartType; use crate::{ os::task::thread::{RewindResultType, WasiMemoryLayout}, - syscalls::thread_spawn_internal_phase2, + syscalls::thread_spawn_internal_using_layout, RewindState, }; @@ -62,7 +62,7 @@ impl JournalEffector { )?); // Now spawn the thread itself - thread_spawn_internal_phase2::( + thread_spawn_internal_using_layout::( ctx, thread_handle, layout.clone(), diff --git a/lib/wasix/src/syscalls/wasi/thread_spawn.rs b/lib/wasix/src/syscalls/wasi/thread_spawn.rs index aab3dc64f13..be8e9de02a3 100644 --- a/lib/wasix/src/syscalls/wasi/thread_spawn.rs +++ b/lib/wasix/src/syscalls/wasi/thread_spawn.rs @@ -21,7 +21,7 @@ pub fn thread_spawn( mut ctx: FunctionEnvMut<'_, WasiEnv>, start_ptr: WasmPtr, M>, ) -> i32 { - thread_spawn_internal_phase1(&mut ctx, start_ptr) + thread_spawn_internal_from_wasi(&mut ctx, start_ptr) .map(|tid| tid as i32) .map_err(|errno| errno as i32) .unwrap_or_else(|err| -err) diff --git a/lib/wasix/src/syscalls/wasix/thread_spawn.rs b/lib/wasix/src/syscalls/wasix/thread_spawn.rs index 01dc24b71b6..213804768e7 100644 --- a/lib/wasix/src/syscalls/wasix/thread_spawn.rs +++ b/lib/wasix/src/syscalls/wasix/thread_spawn.rs @@ -34,7 +34,7 @@ pub fn thread_spawn_v2( ret_tid: WasmPtr, ) -> Errno { // Create the thread - let tid = wasi_try!(thread_spawn_internal_phase1(&mut ctx, start_ptr)); + let tid = wasi_try!(thread_spawn_internal_from_wasi(&mut ctx, start_ptr)); // Success let memory = unsafe { ctx.data().memory_view(&ctx) }; @@ -42,7 +42,7 @@ pub fn thread_spawn_v2( Errno::Success } -pub fn thread_spawn_internal_phase1( +pub fn thread_spawn_internal_from_wasi( ctx: &mut FunctionEnvMut<'_, WasiEnv>, start_ptr: WasmPtr, M>, ) -> Result { @@ -90,13 +90,13 @@ pub fn thread_spawn_internal_phase1( Span::current().record("tid", thread_id); // Spawn the thread - thread_spawn_internal_phase2::(ctx, thread_handle, layout, start_ptr_offset, None)?; + thread_spawn_internal_using_layout::(ctx, thread_handle, layout, start_ptr_offset, None)?; // Success Ok(thread_id) } -pub fn thread_spawn_internal_phase2( +pub fn thread_spawn_internal_using_layout( ctx: &mut FunctionEnvMut<'_, WasiEnv>, thread_handle: Arc, layout: WasiMemoryLayout, From f68f58dc733bebe8ac5719038ec70a3d8dcc446b Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 09:29:34 +1100 Subject: [PATCH 48/67] Renamed the journal syscall player --- lib/wasix/src/syscalls/journal/actions/close_thread.rs | 2 +- lib/wasix/src/syscalls/journal/actions/fd_advise.rs | 2 +- lib/wasix/src/syscalls/journal/actions/fd_allocate.rs | 2 +- lib/wasix/src/syscalls/journal/actions/fd_close.rs | 2 +- lib/wasix/src/syscalls/journal/actions/fd_dup.rs | 2 +- lib/wasix/src/syscalls/journal/actions/fd_open.rs | 2 +- lib/wasix/src/syscalls/journal/actions/fd_renumber.rs | 2 +- lib/wasix/src/syscalls/journal/actions/fd_seek.rs | 2 +- lib/wasix/src/syscalls/journal/actions/fd_set_flags.rs | 2 +- lib/wasix/src/syscalls/journal/actions/fd_set_rights.rs | 2 +- lib/wasix/src/syscalls/journal/actions/fd_set_size.rs | 2 +- lib/wasix/src/syscalls/journal/actions/fd_set_times.rs | 2 +- lib/wasix/src/syscalls/journal/actions/fd_write.rs | 2 +- lib/wasix/src/syscalls/journal/actions/init_module.rs | 2 +- lib/wasix/src/syscalls/journal/actions/mod.rs | 4 ++-- lib/wasix/src/syscalls/journal/actions/path_set_times.rs | 2 +- lib/wasix/src/syscalls/journal/actions/process_exit.rs | 2 +- lib/wasix/src/syscalls/journal/actions/set_thread.rs | 2 +- lib/wasix/src/syscalls/journal/actions/snapshot.rs | 2 +- lib/wasix/src/syscalls/journal/actions/tty_set.rs | 2 +- lib/wasix/src/syscalls/journal/actions/update_memory.rs | 2 +- lib/wasix/src/syscalls/journal/clear_ethereal.rs | 2 +- lib/wasix/src/syscalls/journal/mod.rs | 6 +++--- lib/wasix/src/syscalls/journal/play_event.rs | 2 +- lib/wasix/src/syscalls/journal/restore_snapshot.rs | 2 +- 25 files changed, 28 insertions(+), 28 deletions(-) diff --git a/lib/wasix/src/syscalls/journal/actions/close_thread.rs b/lib/wasix/src/syscalls/journal/actions/close_thread.rs index 3c20cf88e02..cefdff780e6 100644 --- a/lib/wasix/src/syscalls/journal/actions/close_thread.rs +++ b/lib/wasix/src/syscalls/journal/actions/close_thread.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_close_thread( &mut self, id: u32, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_advise.rs b/lib/wasix/src/syscalls/journal/actions/fd_advise.rs index b3d40dec2ed..a888705ec0f 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_advise.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_advise.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_fd_advise( &mut self, fd: Fd, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_allocate.rs b/lib/wasix/src/syscalls/journal/actions/fd_allocate.rs index 0831bcdef70..8f7125bcd49 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_allocate.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_allocate.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_fd_allocate( &mut self, fd: Fd, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_close.rs b/lib/wasix/src/syscalls/journal/actions/fd_close.rs index 23cad8ff0fa..1cb5df47f29 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_close.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_close.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_fd_close(&mut self, fd: u32) -> Result<(), WasiRuntimeError> { tracing::trace!(%fd, "Replay journal - FdClose"); self.stdout_fds.remove(&fd); diff --git a/lib/wasix/src/syscalls/journal/actions/fd_dup.rs b/lib/wasix/src/syscalls/journal/actions/fd_dup.rs index a3ad9d75691..cc1fd2d5a63 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_dup.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_dup.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_fd_dup( &mut self, original_fd: u32, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_open.rs b/lib/wasix/src/syscalls/journal/actions/fd_open.rs index aa48c6b8286..64dc09ff89f 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_open.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_open.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_fd_open( &mut self, fd: u32, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_renumber.rs b/lib/wasix/src/syscalls/journal/actions/fd_renumber.rs index d1adb0fc24d..8656dd0772b 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_renumber.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_renumber.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_fd_renumber( &mut self, old_fd: u32, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_seek.rs b/lib/wasix/src/syscalls/journal/actions/fd_seek.rs index 0545dedd205..f44a7a19287 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_seek.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_seek.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_fd_seek( &mut self, fd: u32, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_set_flags.rs b/lib/wasix/src/syscalls/journal/actions/fd_set_flags.rs index 932a224be43..525a63b62d6 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_set_flags.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_set_flags.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_fd_set_flags( &mut self, fd: Fd, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_set_rights.rs b/lib/wasix/src/syscalls/journal/actions/fd_set_rights.rs index 54d3601c22f..1ea0e6f34b4 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_set_rights.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_set_rights.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_fd_set_rights( &mut self, fd: Fd, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_set_size.rs b/lib/wasix/src/syscalls/journal/actions/fd_set_size.rs index 46f940edbaa..d36fed586ce 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_set_size.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_set_size.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_fd_set_size( &mut self, fd: Fd, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_set_times.rs b/lib/wasix/src/syscalls/journal/actions/fd_set_times.rs index fe7964efffc..1cd0bf3c4ee 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_set_times.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_set_times.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_fd_set_times( &mut self, fd: Fd, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_write.rs b/lib/wasix/src/syscalls/journal/actions/fd_write.rs index 4af6fdb91ec..590d6caf2b3 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_write.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_write.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_fd_write( &mut self, fd: u32, diff --git a/lib/wasix/src/syscalls/journal/actions/init_module.rs b/lib/wasix/src/syscalls/journal/actions/init_module.rs index 696b3079080..d8f476bab00 100644 --- a/lib/wasix/src/syscalls/journal/actions/init_module.rs +++ b/lib/wasix/src/syscalls/journal/actions/init_module.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_init_module( &mut self, wasm_hash: [u8; 8], diff --git a/lib/wasix/src/syscalls/journal/actions/mod.rs b/lib/wasix/src/syscalls/journal/actions/mod.rs index fb8e0a8e305..49a5d1d6a42 100644 --- a/lib/wasix/src/syscalls/journal/actions/mod.rs +++ b/lib/wasix/src/syscalls/journal/actions/mod.rs @@ -8,6 +8,7 @@ mod fd_renumber; mod fd_seek; mod fd_set_flags; mod fd_set_rights; +mod fd_set_size; mod fd_set_times; mod fd_write; mod init_module; @@ -17,11 +18,10 @@ mod set_thread; mod snapshot; mod tty_set; mod update_memory; -mod fd_set_size; use crate::journal::JournalEffector; use crate::syscalls::anyhow_err_to_runtime_err; -use crate::syscalls::JournalReplayRunner; +use crate::syscalls::JournalSyscallPlayer; use crate::RewindState; use crate::WasiRuntimeError; use crate::WasiThreadId; diff --git a/lib/wasix/src/syscalls/journal/actions/path_set_times.rs b/lib/wasix/src/syscalls/journal/actions/path_set_times.rs index 299e6115929..d191f89e319 100644 --- a/lib/wasix/src/syscalls/journal/actions/path_set_times.rs +++ b/lib/wasix/src/syscalls/journal/actions/path_set_times.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_path_set_times( &mut self, fd: Fd, diff --git a/lib/wasix/src/syscalls/journal/actions/process_exit.rs b/lib/wasix/src/syscalls/journal/actions/process_exit.rs index a797b6e9534..0089c3935f9 100644 --- a/lib/wasix/src/syscalls/journal/actions/process_exit.rs +++ b/lib/wasix/src/syscalls/journal/actions/process_exit.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_process_exit( &mut self, exit_code: Option, diff --git a/lib/wasix/src/syscalls/journal/actions/set_thread.rs b/lib/wasix/src/syscalls/journal/actions/set_thread.rs index fae5edd90a1..854c648036e 100644 --- a/lib/wasix/src/syscalls/journal/actions/set_thread.rs +++ b/lib/wasix/src/syscalls/journal/actions/set_thread.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_set_thread( &mut self, id: u32, diff --git a/lib/wasix/src/syscalls/journal/actions/snapshot.rs b/lib/wasix/src/syscalls/journal/actions/snapshot.rs index 34f912fea32..253f4174a5f 100644 --- a/lib/wasix/src/syscalls/journal/actions/snapshot.rs +++ b/lib/wasix/src/syscalls/journal/actions/snapshot.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_snapshot( &mut self, when: SystemTime, diff --git a/lib/wasix/src/syscalls/journal/actions/tty_set.rs b/lib/wasix/src/syscalls/journal/actions/tty_set.rs index 65406396272..aa7b85a00cb 100644 --- a/lib/wasix/src/syscalls/journal/actions/tty_set.rs +++ b/lib/wasix/src/syscalls/journal/actions/tty_set.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_tty_set( &mut self, tty: Tty, diff --git a/lib/wasix/src/syscalls/journal/actions/update_memory.rs b/lib/wasix/src/syscalls/journal/actions/update_memory.rs index 37f5b897b0c..5c5be23073a 100644 --- a/lib/wasix/src/syscalls/journal/actions/update_memory.rs +++ b/lib/wasix/src/syscalls/journal/actions/update_memory.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(crate) unsafe fn action_update_memory( &mut self, region: Range, diff --git a/lib/wasix/src/syscalls/journal/clear_ethereal.rs b/lib/wasix/src/syscalls/journal/clear_ethereal.rs index c934cbb44f7..95deb1e039b 100644 --- a/lib/wasix/src/syscalls/journal/clear_ethereal.rs +++ b/lib/wasix/src/syscalls/journal/clear_ethereal.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(super) fn clear_ethereal( &mut self, mut differ_ethereal: Option<&mut Vec>>, diff --git a/lib/wasix/src/syscalls/journal/mod.rs b/lib/wasix/src/syscalls/journal/mod.rs index 4c31455a561..571b11dd903 100644 --- a/lib/wasix/src/syscalls/journal/mod.rs +++ b/lib/wasix/src/syscalls/journal/mod.rs @@ -24,7 +24,7 @@ use std::{collections::BTreeMap, ops::Range}; use super::*; -pub struct JournalReplayRunner<'a, 'c> { +pub struct JournalSyscallPlayer<'a, 'c> { pub ctx: FunctionEnvMut<'c, WasiEnv>, pub bootstrapping: bool, @@ -47,10 +47,10 @@ pub struct JournalReplayRunner<'a, 'c> { pub stderr_fds: HashSet, } -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub fn new(mut ctx: FunctionEnvMut<'c, WasiEnv>, bootstrapping: bool) -> Self { let cur_module_hash: [u8; 8] = ctx.data().process.module_hash.as_bytes(); - let mut ret = JournalReplayRunner { + let mut ret = JournalSyscallPlayer { ctx, bootstrapping, cur_module_hash, diff --git a/lib/wasix/src/syscalls/journal/play_event.rs b/lib/wasix/src/syscalls/journal/play_event.rs index 0815cfbfdac..e9cb1ddc15a 100644 --- a/lib/wasix/src/syscalls/journal/play_event.rs +++ b/lib/wasix/src/syscalls/journal/play_event.rs @@ -2,7 +2,7 @@ use std::ops::Range; use super::*; -impl<'a, 'c> JournalReplayRunner<'a, 'c> { +impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { pub(super) unsafe fn play_event( &mut self, next: JournalEntry<'a>, diff --git a/lib/wasix/src/syscalls/journal/restore_snapshot.rs b/lib/wasix/src/syscalls/journal/restore_snapshot.rs index e2026f14014..6535ebf4353 100644 --- a/lib/wasix/src/syscalls/journal/restore_snapshot.rs +++ b/lib/wasix/src/syscalls/journal/restore_snapshot.rs @@ -15,7 +15,7 @@ pub unsafe fn restore_snapshot( use crate::{journal::Journal, os::task::process::MemorySnapshotRegion}; // Create the journal replay runner - let mut runner = JournalReplayRunner::new(ctx, bootstrapping); + let mut runner = JournalSyscallPlayer::new(ctx, bootstrapping); // We read all the logs from the journal into the state machine let mut ethereal_events = Vec::new(); From bd95ef334a54174ed1b460ce9dda4e07d7160dad Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 10:02:03 +1100 Subject: [PATCH 49/67] Added another struct for RemoteSocket to make it simplier --- lib/wasix/src/fs/mod.rs | 4 +- .../journal/effector/syscalls/sock_accept.rs | 43 +-- .../journal/effector/syscalls/sock_connect.rs | 42 +-- lib/wasix/src/net/socket.rs | 295 ++++++------------ lib/wasix/src/syscalls/wasix/sock_open.rs | 36 ++- 5 files changed, 165 insertions(+), 255 deletions(-) diff --git a/lib/wasix/src/fs/mod.rs b/lib/wasix/src/fs/mod.rs index 2ff376da3d7..a8a910422cb 100644 --- a/lib/wasix/src/fs/mod.rs +++ b/lib/wasix/src/fs/mod.rs @@ -1380,10 +1380,10 @@ impl WasiFs { Kind::File { .. } => Filetype::RegularFile, Kind::Dir { .. } => Filetype::Directory, Kind::Symlink { .. } => Filetype::SymbolicLink, - Kind::Socket { socket } => match socket.inner.protected.read().unwrap().kind { + Kind::Socket { socket } => match &socket.inner.protected.read().unwrap().kind { InodeSocketKind::TcpStream { .. } => Filetype::SocketStream, InodeSocketKind::Raw { .. } => Filetype::SocketRaw, - InodeSocketKind::PreSocket { ty, .. } => match ty { + InodeSocketKind::PreSocket { props, .. } => match props.ty { Socktype::Stream => Filetype::SocketStream, Socktype::Dgram => Filetype::SocketDgram, Socktype::Raw => Filetype::SocketRaw, diff --git a/lib/wasix/src/journal/effector/syscalls/sock_accept.rs b/lib/wasix/src/journal/effector/syscalls/sock_accept.rs index eebb61d9423..b66d4407d00 100644 --- a/lib/wasix/src/journal/effector/syscalls/sock_accept.rs +++ b/lib/wasix/src/journal/effector/syscalls/sock_accept.rs @@ -4,7 +4,7 @@ use wasmer_wasix_types::wasi::{Addressfamily, SockProto, Socktype}; use crate::{ fs::Kind, - net::socket::{InodeSocket, InodeSocketKind}, + net::socket::{InodeSocket, InodeSocketKind, SocketProperties}, }; use super::*; @@ -45,28 +45,29 @@ impl JournalEffector { socket: InodeSocket::new(InodeSocketKind::RemoteSocket { local_addr: addr, peer_addr, - family: match peer_addr.is_ipv4() { - true => Addressfamily::Inet4, - false => Addressfamily::Inet6, - }, - ty: Socktype::Stream, - pt: SockProto::Tcp, - - only_v6: false, - reuse_port: false, - reuse_addr: false, ttl: 0, multicast_ttl: 0, - no_delay: None, - keep_alive: None, - dont_route: None, - send_buf_size: None, - recv_buf_size: None, - write_timeout: None, - read_timeout: None, - accept_timeout: None, - connect_timeout: None, - handler: None, + props: SocketProperties { + family: match peer_addr.is_ipv4() { + true => Addressfamily::Inet4, + false => Addressfamily::Inet6, + }, + ty: Socktype::Stream, + pt: SockProto::Tcp, + only_v6: false, + reuse_port: false, + reuse_addr: false, + no_delay: None, + keep_alive: None, + dont_route: None, + send_buf_size: None, + recv_buf_size: None, + write_timeout: None, + read_timeout: None, + accept_timeout: None, + connect_timeout: None, + handler: None, + }, }), }; diff --git a/lib/wasix/src/journal/effector/syscalls/sock_connect.rs b/lib/wasix/src/journal/effector/syscalls/sock_connect.rs index 4be134f5423..a72763497b6 100644 --- a/lib/wasix/src/journal/effector/syscalls/sock_connect.rs +++ b/lib/wasix/src/journal/effector/syscalls/sock_connect.rs @@ -4,7 +4,7 @@ use wasmer_wasix_types::wasi::{Addressfamily, SockProto, Socktype}; use crate::{ fs::Kind, - net::socket::{InodeSocket, InodeSocketKind}, + net::socket::{InodeSocket, InodeSocketKind, SocketProperties}, }; use super::*; @@ -36,27 +36,29 @@ impl JournalEffector { socket: InodeSocket::new(InodeSocketKind::RemoteSocket { local_addr, peer_addr, - family: match peer_addr.is_ipv4() { - true => Addressfamily::Inet4, - false => Addressfamily::Inet6, - }, - ty: Socktype::Stream, - pt: SockProto::Tcp, - only_v6: false, - reuse_port: false, - reuse_addr: false, ttl: 0, multicast_ttl: 0, - no_delay: None, - keep_alive: None, - dont_route: None, - send_buf_size: None, - recv_buf_size: None, - write_timeout: None, - read_timeout: None, - accept_timeout: None, - connect_timeout: None, - handler: None, + props: SocketProperties { + family: match peer_addr.is_ipv4() { + true => Addressfamily::Inet4, + false => Addressfamily::Inet6, + }, + ty: Socktype::Stream, + pt: SockProto::Tcp, + only_v6: false, + reuse_port: false, + reuse_addr: false, + no_delay: None, + keep_alive: None, + dont_route: None, + send_buf_size: None, + recv_buf_size: None, + write_timeout: None, + read_timeout: None, + accept_timeout: None, + connect_timeout: None, + handler: None, + }, }), }; diff --git a/lib/wasix/src/net/socket.rs b/lib/wasix/src/net/socket.rs index 166c1161946..bbf6c8dd9e9 100644 --- a/lib/wasix/src/net/socket.rs +++ b/lib/wasix/src/net/socket.rs @@ -35,27 +35,32 @@ pub enum InodeHttpSocketType { #[derive(Derivative)] #[derivative(Debug)] +pub struct SocketProperties { + pub family: Addressfamily, + pub ty: Socktype, + pub pt: SockProto, + pub only_v6: bool, + pub reuse_port: bool, + pub reuse_addr: bool, + pub no_delay: Option, + pub keep_alive: Option, + pub dont_route: Option, + pub send_buf_size: Option, + pub recv_buf_size: Option, + pub write_timeout: Option, + pub read_timeout: Option, + pub accept_timeout: Option, + pub connect_timeout: Option, + #[derivative(Debug = "ignore")] + pub handler: Option>, +} + +#[derive(Debug)] //#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub enum InodeSocketKind { PreSocket { - family: Addressfamily, - ty: Socktype, - pt: SockProto, + props: SocketProperties, addr: Option, - only_v6: bool, - reuse_port: bool, - reuse_addr: bool, - no_delay: Option, - keep_alive: Option, - dont_route: Option, - send_buf_size: Option, - recv_buf_size: Option, - write_timeout: Option, - read_timeout: Option, - accept_timeout: Option, - connect_timeout: Option, - #[derivative(Debug = "ignore")] - handler: Option>, }, Icmp(Box), Raw(Box), @@ -73,27 +78,11 @@ pub enum InodeSocketKind { peer: Option, }, RemoteSocket { - family: Addressfamily, - ty: Socktype, - pt: SockProto, + props: SocketProperties, local_addr: SocketAddr, peer_addr: SocketAddr, - only_v6: bool, - reuse_port: bool, - reuse_addr: bool, ttl: u32, multicast_ttl: u32, - no_delay: Option, - keep_alive: Option, - dont_route: Option, - send_buf_size: Option, - recv_buf_size: Option, - write_timeout: Option, - read_timeout: Option, - accept_timeout: Option, - connect_timeout: Option, - #[derivative(Debug = "ignore")] - handler: Option>, }, } @@ -261,15 +250,8 @@ impl InodeSocket { let socket = { let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { - InodeSocketKind::PreSocket { - family, - ty, - addr, - reuse_port, - reuse_addr, - .. - } => { - match *family { + InodeSocketKind::PreSocket { props, addr, .. } => { + match props.family { Addressfamily::Inet4 => { if !set_addr.is_ipv4() { tracing::debug!( @@ -294,15 +276,15 @@ impl InodeSocket { addr.replace(set_addr); let addr = (*addr).unwrap(); - match *ty { + match props.ty { Socktype::Stream => { // we already set the socket address - next we need a listen or connect so nothing // more to do at this time return Ok(None); } Socktype::Dgram => { - let reuse_port = *reuse_port; - let reuse_addr = *reuse_addr; + let reuse_port = props.reuse_port; + let reuse_addr = props.reuse_addr; drop(inner); net.bind_udp(addr, reuse_port, reuse_addr) @@ -311,14 +293,11 @@ impl InodeSocket { } } InodeSocketKind::RemoteSocket { - family, - ty, + props, local_addr: addr, - reuse_port, - reuse_addr, .. } => { - match *family { + match props.family { Addressfamily::Inet4 => { if !set_addr.is_ipv4() { tracing::debug!( @@ -343,15 +322,15 @@ impl InodeSocket { *addr = set_addr; let addr = *addr; - match *ty { + match props.ty { Socktype::Stream => { // we already set the socket address - next we need a listen or connect so nothing // more to do at this time return Ok(None); } Socktype::Dgram => { - let reuse_port = *reuse_port; - let reuse_addr = *reuse_addr; + let reuse_port = props.reuse_port; + let reuse_addr = props.reuse_addr; drop(inner); net.bind_udp(addr, reuse_port, reuse_addr) @@ -387,23 +366,16 @@ impl InodeSocket { let socket = { let inner = self.inner.protected.read().unwrap(); match &inner.kind { - InodeSocketKind::PreSocket { - ty, - addr, - only_v6, - reuse_port, - reuse_addr, - .. - } => match *ty { + InodeSocketKind::PreSocket { props, addr, .. } => match props.ty { Socktype::Stream => { if addr.is_none() { tracing::warn!("wasi[?]::sock_listen - failed - address not set"); return Err(Errno::Inval); } let addr = *addr.as_ref().unwrap(); - let only_v6 = *only_v6; - let reuse_port = *reuse_port; - let reuse_addr = *reuse_addr; + let only_v6 = props.only_v6; + let reuse_port = props.reuse_port; + let reuse_addr = props.reuse_addr; drop(inner); net.listen_tcp(addr, only_v6, reuse_port, reuse_addr) @@ -417,18 +389,15 @@ impl InodeSocket { } }, InodeSocketKind::RemoteSocket { - ty, + props, local_addr: addr, - only_v6, - reuse_port, - reuse_addr, .. - } => match *ty { + } => match props.ty { Socktype::Stream => { let addr = *addr; - let only_v6 = *only_v6; - let reuse_port = *reuse_port; - let reuse_addr = *reuse_addr; + let only_v6 = props.only_v6; + let reuse_port = props.reuse_port; + let reuse_addr = props.reuse_addr; drop(inner); net.listen_tcp(addr, only_v6, reuse_port, reuse_addr) @@ -577,25 +546,15 @@ impl InodeSocket { let connect = { let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { - InodeSocketKind::PreSocket { - ty, - addr, - write_timeout, - read_timeout, - no_delay, - keep_alive, - dont_route, - handler: h, - .. - } => { - handler = h.take(); - new_write_timeout = *write_timeout; - new_read_timeout = *read_timeout; - match *ty { + InodeSocketKind::PreSocket { props, addr, .. } => { + handler = props.handler.take(); + new_write_timeout = props.write_timeout; + new_read_timeout = props.read_timeout; + match props.ty { Socktype::Stream => { - let no_delay = *no_delay; - let keep_alive = *keep_alive; - let dont_route = *dont_route; + let no_delay = props.no_delay; + let keep_alive = props.keep_alive; + let dont_route = props.dont_route; let addr = match addr { Some(a) => *a, None => { @@ -673,12 +632,12 @@ impl InodeSocket { pub fn addr_local(&self) -> Result { let inner = self.inner.protected.read().unwrap(); Ok(match &inner.kind { - InodeSocketKind::PreSocket { family, addr, .. } => { + InodeSocketKind::PreSocket { props, addr, .. } => { if let Some(addr) = addr { *addr } else { SocketAddr::new( - match *family { + match props.family { Addressfamily::Inet4 => IpAddr::V4(Ipv4Addr::UNSPECIFIED), Addressfamily::Inet6 => IpAddr::V6(Ipv6Addr::UNSPECIFIED), _ => return Err(Errno::Inval), @@ -707,8 +666,8 @@ impl InodeSocket { pub fn addr_peer(&self) -> Result { let inner = self.inner.protected.read().unwrap(); Ok(match &inner.kind { - InodeSocketKind::PreSocket { family, .. } => SocketAddr::new( - match *family { + InodeSocketKind::PreSocket { props, .. } => SocketAddr::new( + match props.family { Addressfamily::Inet4 => IpAddr::V4(Ipv4Addr::UNSPECIFIED), Addressfamily::Inet6 => IpAddr::V6(Ipv6Addr::UNSPECIFIED), _ => return Err(Errno::Inval), @@ -744,31 +703,15 @@ impl InodeSocket { pub fn set_opt_flag(&mut self, option: WasiSocketOption, val: bool) -> Result<(), Errno> { let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { - InodeSocketKind::PreSocket { - only_v6, - reuse_port, - reuse_addr, - no_delay, - keep_alive, - dont_route, - .. - } - | InodeSocketKind::RemoteSocket { - only_v6, - reuse_port, - reuse_addr, - no_delay, - keep_alive, - dont_route, - .. - } => { + InodeSocketKind::PreSocket { props, .. } + | InodeSocketKind::RemoteSocket { props, .. } => { match option { - WasiSocketOption::OnlyV6 => *only_v6 = val, - WasiSocketOption::ReusePort => *reuse_port = val, - WasiSocketOption::ReuseAddr => *reuse_addr = val, - WasiSocketOption::NoDelay => *no_delay = Some(val), - WasiSocketOption::KeepAlive => *keep_alive = Some(val), - WasiSocketOption::DontRoute => *dont_route = Some(val), + WasiSocketOption::OnlyV6 => props.only_v6 = val, + WasiSocketOption::ReusePort => props.reuse_port = val, + WasiSocketOption::ReuseAddr => props.reuse_addr = val, + WasiSocketOption::NoDelay => props.no_delay = Some(val), + WasiSocketOption::KeepAlive => props.keep_alive = Some(val), + WasiSocketOption::DontRoute => props.dont_route = Some(val), _ => return Err(Errno::Inval), }; } @@ -811,27 +754,13 @@ impl InodeSocket { pub fn get_opt_flag(&self, option: WasiSocketOption) -> Result { let mut inner = self.inner.protected.write().unwrap(); Ok(match &mut inner.kind { - InodeSocketKind::PreSocket { - only_v6, - reuse_port, - reuse_addr, - no_delay, - keep_alive, - .. - } - | InodeSocketKind::RemoteSocket { - only_v6, - reuse_port, - reuse_addr, - no_delay, - keep_alive, - .. - } => match option { - WasiSocketOption::OnlyV6 => *only_v6, - WasiSocketOption::ReusePort => *reuse_port, - WasiSocketOption::ReuseAddr => *reuse_addr, - WasiSocketOption::NoDelay => no_delay.unwrap_or_default(), - WasiSocketOption::KeepAlive => keep_alive.unwrap_or_default(), + InodeSocketKind::PreSocket { props, .. } + | InodeSocketKind::RemoteSocket { props, .. } => match option { + WasiSocketOption::OnlyV6 => props.only_v6, + WasiSocketOption::ReusePort => props.reuse_port, + WasiSocketOption::ReuseAddr => props.reuse_addr, + WasiSocketOption::NoDelay => props.no_delay.unwrap_or_default(), + WasiSocketOption::KeepAlive => props.keep_alive.unwrap_or_default(), _ => return Err(Errno::Inval), }, InodeSocketKind::Raw(sock) => match option { @@ -869,9 +798,9 @@ impl InodeSocket { pub fn set_send_buf_size(&mut self, size: usize) -> Result<(), Errno> { let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { - InodeSocketKind::PreSocket { send_buf_size, .. } - | InodeSocketKind::RemoteSocket { send_buf_size, .. } => { - *send_buf_size = Some(size); + InodeSocketKind::PreSocket { props, .. } + | InodeSocketKind::RemoteSocket { props, .. } => { + props.send_buf_size = Some(size); } InodeSocketKind::TcpStream { socket, .. } => { socket @@ -886,9 +815,9 @@ impl InodeSocket { pub fn send_buf_size(&self) -> Result { let inner = self.inner.protected.read().unwrap(); match &inner.kind { - InodeSocketKind::PreSocket { send_buf_size, .. } - | InodeSocketKind::RemoteSocket { send_buf_size, .. } => { - Ok((*send_buf_size).unwrap_or_default()) + InodeSocketKind::PreSocket { props, .. } + | InodeSocketKind::RemoteSocket { props, .. } => { + Ok(props.send_buf_size.unwrap_or_default()) } InodeSocketKind::TcpStream { socket, .. } => { socket.send_buf_size().map_err(net_error_into_wasi_err) @@ -900,9 +829,9 @@ impl InodeSocket { pub fn set_recv_buf_size(&mut self, size: usize) -> Result<(), Errno> { let mut inner = self.inner.protected.write().unwrap(); match &mut inner.kind { - InodeSocketKind::PreSocket { recv_buf_size, .. } - | InodeSocketKind::RemoteSocket { recv_buf_size, .. } => { - *recv_buf_size = Some(size); + InodeSocketKind::PreSocket { props, .. } + | InodeSocketKind::RemoteSocket { props, .. } => { + props.recv_buf_size = Some(size); } InodeSocketKind::TcpStream { socket, .. } => { socket @@ -917,9 +846,9 @@ impl InodeSocket { pub fn recv_buf_size(&self) -> Result { let inner = self.inner.protected.read().unwrap(); match &inner.kind { - InodeSocketKind::PreSocket { recv_buf_size, .. } - | InodeSocketKind::RemoteSocket { recv_buf_size, .. } => { - Ok((*recv_buf_size).unwrap_or_default()) + InodeSocketKind::PreSocket { props, .. } + | InodeSocketKind::RemoteSocket { props, .. } => { + Ok(props.recv_buf_size.unwrap_or_default()) } InodeSocketKind::TcpStream { socket, .. } => { socket.recv_buf_size().map_err(net_error_into_wasi_err) @@ -977,25 +906,13 @@ impl InodeSocket { } Ok(()) } - InodeSocketKind::PreSocket { - read_timeout, - write_timeout, - connect_timeout, - accept_timeout, - .. - } - | InodeSocketKind::RemoteSocket { - read_timeout, - write_timeout, - connect_timeout, - accept_timeout, - .. - } => { + InodeSocketKind::PreSocket { props, .. } + | InodeSocketKind::RemoteSocket { props, .. } => { match ty { - TimeType::ConnectTimeout => *connect_timeout = timeout, - TimeType::AcceptTimeout => *accept_timeout = timeout, - TimeType::ReadTimeout => *read_timeout = timeout, - TimeType::WriteTimeout => *write_timeout = timeout, + TimeType::ConnectTimeout => props.connect_timeout = timeout, + TimeType::AcceptTimeout => props.accept_timeout = timeout, + TimeType::ReadTimeout => props.read_timeout = timeout, + TimeType::WriteTimeout => props.write_timeout = timeout, _ => return Err(Errno::Io), } Ok(()) @@ -1020,24 +937,12 @@ impl InodeSocket { TimeType::AcceptTimeout => *accept_timeout, _ => return Err(Errno::Inval), }), - InodeSocketKind::PreSocket { - read_timeout, - write_timeout, - connect_timeout, - accept_timeout, - .. - } - | InodeSocketKind::RemoteSocket { - read_timeout, - write_timeout, - connect_timeout, - accept_timeout, - .. - } => match ty { - TimeType::ConnectTimeout => Ok(*connect_timeout), - TimeType::AcceptTimeout => Ok(*accept_timeout), - TimeType::ReadTimeout => Ok(*read_timeout), - TimeType::WriteTimeout => Ok(*write_timeout), + InodeSocketKind::PreSocket { props, .. } + | InodeSocketKind::RemoteSocket { props, .. } => match ty { + TimeType::ConnectTimeout => Ok(props.connect_timeout), + TimeType::AcceptTimeout => Ok(props.accept_timeout), + TimeType::ReadTimeout => Ok(props.read_timeout), + TimeType::WriteTimeout => Ok(props.write_timeout), _ => Err(Errno::Inval), }, _ => Err(Errno::Notsup), @@ -1524,11 +1429,11 @@ impl InodeSocketProtected { InodeSocketKind::UdpSocket { socket, .. } => socket.remove_handler(), InodeSocketKind::Raw(socket) => socket.remove_handler(), InodeSocketKind::Icmp(socket) => socket.remove_handler(), - InodeSocketKind::PreSocket { handler, .. } => { - handler.take(); + InodeSocketKind::PreSocket { props, .. } => { + props.handler.take(); } - InodeSocketKind::RemoteSocket { handler, .. } => { - handler.take(); + InodeSocketKind::RemoteSocket { props, .. } => { + props.handler.take(); } } } @@ -1569,9 +1474,9 @@ impl InodeSocketProtected { InodeSocketKind::UdpSocket { socket, .. } => socket.set_handler(handler), InodeSocketKind::Raw(socket) => socket.set_handler(handler), InodeSocketKind::Icmp(socket) => socket.set_handler(handler), - InodeSocketKind::PreSocket { handler: h, .. } - | InodeSocketKind::RemoteSocket { handler: h, .. } => { - h.replace(handler); + InodeSocketKind::PreSocket { props, .. } + | InodeSocketKind::RemoteSocket { props, .. } => { + props.handler.replace(handler); Ok(()) } } diff --git a/lib/wasix/src/syscalls/wasix/sock_open.rs b/lib/wasix/src/syscalls/wasix/sock_open.rs index dfd0678d8fd..02eb651c8de 100644 --- a/lib/wasix/src/syscalls/wasix/sock_open.rs +++ b/lib/wasix/src/syscalls/wasix/sock_open.rs @@ -1,5 +1,5 @@ use super::*; -use crate::syscalls::*; +use crate::{net::socket::SocketProperties, syscalls::*}; /// ### `sock_open()` /// Create an endpoint for communication. @@ -72,23 +72,25 @@ pub(crate) fn sock_open_internal( let kind = match ty { Socktype::Stream | Socktype::Dgram => Kind::Socket { socket: InodeSocket::new(InodeSocketKind::PreSocket { - family: af, - ty, - pt, + props: SocketProperties { + family: af, + ty, + pt, + only_v6: false, + reuse_port: false, + reuse_addr: false, + no_delay: None, + keep_alive: None, + dont_route: None, + send_buf_size: None, + recv_buf_size: None, + write_timeout: None, + read_timeout: None, + accept_timeout: None, + connect_timeout: None, + handler: None, + }, addr: None, - only_v6: false, - reuse_port: false, - reuse_addr: false, - no_delay: None, - keep_alive: None, - dont_route: None, - send_buf_size: None, - recv_buf_size: None, - write_timeout: None, - read_timeout: None, - accept_timeout: None, - connect_timeout: None, - handler: None, }), }, _ => return Ok(Err(Errno::Notsup)), From 9b38ee5abf78847473d18bb3a370869b9f7abdff Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 10:04:37 +1100 Subject: [PATCH 50/67] Added a comment on one of the apply thread methods --- lib/wasix/src/journal/effector/thread_state.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/wasix/src/journal/effector/thread_state.rs b/lib/wasix/src/journal/effector/thread_state.rs index 1658aa2268f..0c824b6bb3c 100644 --- a/lib/wasix/src/journal/effector/thread_state.rs +++ b/lib/wasix/src/journal/effector/thread_state.rs @@ -34,6 +34,10 @@ impl JournalEffector { ) } + /// This will take the supplied stacks and apply them to the memory region + /// dedicated to this thread. After that it will spawn a WASM thread and + // continue the thread where it left off, which may even mean it goes + // straight back to sleep. pub fn apply_thread_state( ctx: &mut FunctionEnvMut<'_, WasiEnv>, tid: WasiThreadId, From 049f5052eedcd4c3b7d250ae511aa9bdcc6b8e57 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 10:09:57 +1100 Subject: [PATCH 51/67] Added comments on VIRUTAL_FD_ROOT --- lib/wasix/src/fs/mod.rs | 13 +++++++++++++ .../effector/syscalls/path_create_directory.rs | 1 + .../effector/syscalls/path_remove_directory.rs | 1 + .../src/journal/effector/syscalls/path_set_times.rs | 1 + .../src/journal/effector/syscalls/path_unlink.rs | 1 + 5 files changed, 17 insertions(+) diff --git a/lib/wasix/src/fs/mod.rs b/lib/wasix/src/fs/mod.rs index a8a910422cb..be36f031c23 100644 --- a/lib/wasix/src/fs/mod.rs +++ b/lib/wasix/src/fs/mod.rs @@ -43,6 +43,18 @@ use crate::syscalls::map_io_err; use crate::{bin_factory::BinaryPackage, state::PreopenedDir, ALL_RIGHTS}; /// the fd value of the virtual root +/// +/// Used for interacting with the file system when it has no +/// pre-opened file descriptors at the root level. Normally +/// a WASM process will do this in the libc initialization stage +/// however that does not happen when the WASM process has never +/// been run. Further that logic could change at any time in libc +/// which would then break functionality. Instead we use this fixed +/// file descriptor +/// +/// This is especially important for fuse mounting journals which +/// use the same syscalls as a normal WASI application but do not +/// run the libc initialization logic pub const VIRTUAL_ROOT_FD: WasiFd = 3; const STDIN_DEFAULT_RIGHTS: Rights = { @@ -1317,6 +1329,7 @@ impl WasiFs { } pub fn get_fd_inode(&self, fd: WasiFd) -> Result { + // see `VIRTUAL_ROOT_FD` for details as to why this exists if fd == VIRTUAL_ROOT_FD { return Ok(self.root_inode.clone()); } diff --git a/lib/wasix/src/journal/effector/syscalls/path_create_directory.rs b/lib/wasix/src/journal/effector/syscalls/path_create_directory.rs index 6400cf70d4d..c44f7f32083 100644 --- a/lib/wasix/src/journal/effector/syscalls/path_create_directory.rs +++ b/lib/wasix/src/journal/effector/syscalls/path_create_directory.rs @@ -26,6 +26,7 @@ impl JournalEffector { fd: Fd, path: &str, ) -> anyhow::Result<()> { + // see `VIRTUAL_ROOT_FD` for details as to why this exists if fd == VIRTUAL_ROOT_FD { ctx.data().state.fs.root_fs.create_dir(&Path::new(path))?; } else { diff --git a/lib/wasix/src/journal/effector/syscalls/path_remove_directory.rs b/lib/wasix/src/journal/effector/syscalls/path_remove_directory.rs index e8c6b1d6d3f..0ef9974e1b1 100644 --- a/lib/wasix/src/journal/effector/syscalls/path_remove_directory.rs +++ b/lib/wasix/src/journal/effector/syscalls/path_remove_directory.rs @@ -26,6 +26,7 @@ impl JournalEffector { fd: Fd, path: &str, ) -> anyhow::Result<()> { + // see `VIRTUAL_ROOT_FD` for details as to why this exists if fd == VIRTUAL_ROOT_FD { ctx.data().state.fs.root_fs.remove_dir(&Path::new(path))?; } else { diff --git a/lib/wasix/src/journal/effector/syscalls/path_set_times.rs b/lib/wasix/src/journal/effector/syscalls/path_set_times.rs index b22d643ee63..440acc2c8e8 100644 --- a/lib/wasix/src/journal/effector/syscalls/path_set_times.rs +++ b/lib/wasix/src/journal/effector/syscalls/path_set_times.rs @@ -34,6 +34,7 @@ impl JournalEffector { st_mtim: Timestamp, fst_flags: Fstflags, ) -> anyhow::Result<()> { + // see `VIRTUAL_ROOT_FD` for details as to why this exists if fd == VIRTUAL_ROOT_FD { // we ignore this record as its not implemented yet } else { diff --git a/lib/wasix/src/journal/effector/syscalls/path_unlink.rs b/lib/wasix/src/journal/effector/syscalls/path_unlink.rs index 4ca11f2297c..f155f741fa0 100644 --- a/lib/wasix/src/journal/effector/syscalls/path_unlink.rs +++ b/lib/wasix/src/journal/effector/syscalls/path_unlink.rs @@ -26,6 +26,7 @@ impl JournalEffector { fd: Fd, path: &str, ) -> anyhow::Result<()> { + // see `VIRTUAL_ROOT_FD` for details as to why this exists if fd == VIRTUAL_ROOT_FD { ctx.data().state.fs.root_fs.remove_file(&Path::new(path))?; } else { From 2184922ef3f0aa2b565469111d4c8ec563a703da Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 10:11:38 +1100 Subject: [PATCH 52/67] Missed one --- lib/wasix/src/journal/effector/syscalls/path_rename.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/wasix/src/journal/effector/syscalls/path_rename.rs b/lib/wasix/src/journal/effector/syscalls/path_rename.rs index 29434e19352..63d66dd6538 100644 --- a/lib/wasix/src/journal/effector/syscalls/path_rename.rs +++ b/lib/wasix/src/journal/effector/syscalls/path_rename.rs @@ -28,6 +28,7 @@ impl JournalEffector { new_fd: Fd, new_path: &str, ) -> anyhow::Result<()> { + // see `VIRTUAL_ROOT_FD` for details as to why this exists if old_fd == VIRTUAL_ROOT_FD && new_fd == VIRTUAL_ROOT_FD { let state = ctx.data().state.clone(); let old_path = old_path.to_string(); From bc520d4d09b077be264d44fe516fb2c3fa9337e7 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 10:16:03 +1100 Subject: [PATCH 53/67] Added additional comments --- lib/wasix/src/fs/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/wasix/src/fs/mod.rs b/lib/wasix/src/fs/mod.rs index be36f031c23..ed5d837d13d 100644 --- a/lib/wasix/src/fs/mod.rs +++ b/lib/wasix/src/fs/mod.rs @@ -423,6 +423,9 @@ fn create_dir_all(fs: &dyn FileSystem, path: &Path) -> Result<(), virtual_fs::Fs Ok(()) } +/// This needs to be exposed so that the multiple use-cases are able +/// to generated unique file descriptors and update the seed during +/// journal restoration #[derive(Debug, Clone)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct WasiFdSeed { From 09672e90e0e7ea9e55f9503a5aadb683b6f0eab4 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 10:31:34 +1100 Subject: [PATCH 54/67] Fixed a whole bunch of linting errors --- lib/virtual-fs/src/mem_fs/file.rs | 8 ++++---- lib/virtual-fs/src/mem_fs/file_opener.rs | 2 +- lib/virtual-fs/src/mem_fs/offloaded_file.rs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/virtual-fs/src/mem_fs/file.rs b/lib/virtual-fs/src/mem_fs/file.rs index 03d11d60fde..43f51b97c1b 100644 --- a/lib/virtual-fs/src/mem_fs/file.rs +++ b/lib/virtual-fs/src/mem_fs/file.rs @@ -153,7 +153,7 @@ impl VirtualFile for FileHandle { let inode = fs.storage.get(self.inode); match inode { Some(Node::File(node)) => node.file.len().try_into().unwrap_or(0), - Some(Node::OffloadedFile(node)) => node.file.len().try_into().unwrap_or(0), + Some(Node::OffloadedFile(node)) => node.file.len(), Some(Node::ReadOnlyFile(node)) => node.file.len().try_into().unwrap_or(0), Some(Node::CustomFile(node)) => { let file = node.file.lock().unwrap(); @@ -186,7 +186,7 @@ impl VirtualFile for FileHandle { metadata.len = new_size; } Some(Node::OffloadedFile(OffloadedFileNode { file, metadata, .. })) => { - file.resize(new_size.try_into().map_err(|_| FsError::UnknownError)?, 0); + file.resize(new_size, 0); metadata.len = new_size; } Some(Node::CustomFile(node)) => { @@ -445,7 +445,7 @@ impl VirtualFile for FileHandle { Some(Node::OffloadedFile(node)) => { node.file .write(OffloadWrite::MmapOffset { offset, size }, &mut cursor)?; - node.metadata.len = node.file.len().try_into().unwrap(); + node.metadata.len = node.file.len(); } _ => { return Err(io::ErrorKind::Unsupported.into()); @@ -868,7 +868,7 @@ impl AsyncWrite for FileHandle { } Some(Node::OffloadedFile(node)) => { let bytes_written = node.file.write(OffloadWrite::Buffer(buf), &mut cursor)?; - node.metadata.len = node.file.len().try_into().unwrap(); + node.metadata.len = node.file.len(); bytes_written } Some(Node::ReadOnlyFile(node)) => { diff --git a/lib/virtual-fs/src/mem_fs/file_opener.rs b/lib/virtual-fs/src/mem_fs/file_opener.rs index 6aa7f0b7321..f2a7da582e7 100644 --- a/lib/virtual-fs/src/mem_fs/file_opener.rs +++ b/lib/virtual-fs/src/mem_fs/file_opener.rs @@ -414,7 +414,7 @@ impl crate::FileOpener for FileSystem { // Move the cursor to the end if needed. if append { - cursor = file.len() as u64; + cursor = file.len(); } } diff --git a/lib/virtual-fs/src/mem_fs/offloaded_file.rs b/lib/virtual-fs/src/mem_fs/offloaded_file.rs index f95e482b767..c30571c2bfc 100644 --- a/lib/virtual-fs/src/mem_fs/offloaded_file.rs +++ b/lib/virtual-fs/src/mem_fs/offloaded_file.rs @@ -174,7 +174,7 @@ impl OffloadedFile { // In this implementation, it's an error to seek beyond the // end of the buffer. let next_cursor = next_cursor.try_into().map_err(to_err)?; - *cursor = cmp::min(self.len() as u64, next_cursor); + *cursor = cmp::min(self.len(), next_cursor); Ok(*cursor) } @@ -183,7 +183,7 @@ impl OffloadedFile { let mut extent_offset = cursor_start; let mut extent_index = 0usize; - while buf.len() > 0 && extent_index < self.extents.len() { + while !buf.is_empty() && extent_index < self.extents.len() { let extent = &self.extents[extent_index]; if extent_offset >= extent.size() { From d064818ec398e0a75f24b6d324ea3d01990891e4 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 11:04:47 +1100 Subject: [PATCH 55/67] Fixed some compile issues for JS --- lib/journal/Cargo.toml | 2 +- lib/virtual-fs/src/mem_fs/offloaded_file.rs | 2 +- lib/wasix/src/os/task/mod.rs | 1 + lib/wasix/src/os/task/process.rs | 38 +++++++++------ lib/wasix/src/os/task/thread.rs | 1 - lib/wasix/src/state/func_env.rs | 3 +- lib/wasix/src/syscalls/journal/mod.rs | 3 ++ lib/wasix/src/syscalls/mod.rs | 54 ++++++++++++--------- 8 files changed, 62 insertions(+), 42 deletions(-) diff --git a/lib/journal/Cargo.toml b/lib/journal/Cargo.toml index 4cc7c6b677b..e3c55a7d0d0 100644 --- a/lib/journal/Cargo.toml +++ b/lib/journal/Cargo.toml @@ -17,7 +17,7 @@ log-file = [ "shared-buffer" ] wasmer = { default-features = false, path = "../api", version = "=4.2.5" } wasmer-wasix-types = { path = "../wasi-types", version = "0.18.0", features = [ "enable-serde" ] } virtual-net = { path = "../virtual-net", version = "0.6.2", default-features = false, features = ["rkyv"] } -virtual-fs = { path = "../virtual-fs" } +virtual-fs = { path = "../virtual-fs", default-features = false } shared-buffer = { workspace = true, optional = true } thiserror = "1" diff --git a/lib/virtual-fs/src/mem_fs/offloaded_file.rs b/lib/virtual-fs/src/mem_fs/offloaded_file.rs index c30571c2bfc..049bd4e0644 100644 --- a/lib/virtual-fs/src/mem_fs/offloaded_file.rs +++ b/lib/virtual-fs/src/mem_fs/offloaded_file.rs @@ -372,7 +372,7 @@ impl OffloadedFile { let diff = extent.size().min(cur_len - new_len); extent.resize(extent.size() - diff); cur_len -= diff; - if extent.size() <= 0 { + if extent.size() == 0 { self.extents.pop(); } } diff --git a/lib/wasix/src/os/task/mod.rs b/lib/wasix/src/os/task/mod.rs index 5839cb9442f..18861b083b3 100644 --- a/lib/wasix/src/os/task/mod.rs +++ b/lib/wasix/src/os/task/mod.rs @@ -6,6 +6,7 @@ pub mod signal; mod task_join_handle; pub mod thread; +#[allow(unused_imports)] pub(crate) use process::WasiProcessInner; pub use task_join_handle::{ OwnedTaskStatus, TaskJoinHandle, TaskStatus, TaskTerminatedError, VirtualTaskHandle, diff --git a/lib/wasix/src/os/task/process.rs b/lib/wasix/src/os/task/process.rs index fcbca171af7..2c0a3b042a2 100644 --- a/lib/wasix/src/os/task/process.rs +++ b/lib/wasix/src/os/task/process.rs @@ -1,8 +1,7 @@ #[cfg(feature = "journal")] -use crate::{journal::JournalEffector, unwind, WasiResult}; +use crate::{journal::JournalEffector, syscalls::do_checkpoint_from_outside, unwind, WasiResult}; use crate::{ - journal::SnapshotTrigger, runtime::module_cache::ModuleHash, - syscalls::do_checkpoint_from_outside, WasiEnv, WasiRuntimeError, + journal::SnapshotTrigger, runtime::module_cache::ModuleHash, WasiEnv, WasiRuntimeError, }; use serde::{Deserialize, Serialize}; #[cfg(feature = "journal")] @@ -171,6 +170,7 @@ pub struct WasiProcessInner { pub wakers: Vec, /// The snapshot memory significantly reduce the amount of /// duplicate entries in the journal for memory that has not changed + #[cfg(feature = "journal")] pub snapshot_memory_hash: HashMap, } @@ -398,7 +398,9 @@ impl WasiProcess { checkpoint: WasiProcessCheckpoint::Execute, wakers: Default::default(), waiting: waiting.clone(), + #[cfg(feature = "journal")] snapshot_on: Default::default(), + #[cfg(feature = "journal")] snapshot_memory_hash: Default::default(), }), Condvar::new(), @@ -666,28 +668,32 @@ impl WasiProcess { /// Signals all the threads in this process fn signal_process_internal(process: &LockableWasiProcessInner, signal: Signal) { + #[allow(unused_mut)] let mut guard = process.0.lock().unwrap(); let pid = guard.pid; tracing::trace!(%pid, "signal-process({:?})", signal); // If the snapshot on ctrl-c is currently registered then we need // to take a snapshot and exit - if signal == Signal::Sigint - && (guard.snapshot_on.contains(&SnapshotTrigger::Sigint) - || guard.snapshot_on.remove(&SnapshotTrigger::FirstSigint)) + #[cfg(feature = "journal")] { - drop(guard); + if signal == Signal::Sigint + && (guard.snapshot_on.contains(&SnapshotTrigger::Sigint) + || guard.snapshot_on.remove(&SnapshotTrigger::FirstSigint)) + { + drop(guard); - tracing::debug!(%pid, "snapshot-on-interrupt-signal"); + tracing::debug!(%pid, "snapshot-on-interrupt-signal"); - do_checkpoint_from_outside( - process, - WasiProcessCheckpoint::Snapshot { - trigger: SnapshotTrigger::Sigint, - }, - ); - return; - }; + do_checkpoint_from_outside( + process, + WasiProcessCheckpoint::Snapshot { + trigger: SnapshotTrigger::Sigint, + }, + ); + return; + }; + } // Check if there are subprocesses that will receive this signal // instead of this process diff --git a/lib/wasix/src/os/task/thread.rs b/lib/wasix/src/os/task/thread.rs index 45fefc8234c..327844d1f3e 100644 --- a/lib/wasix/src/os/task/thread.rs +++ b/lib/wasix/src/os/task/thread.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -#[cfg(feature = "journal")] use std::sync::atomic::{AtomicBool, Ordering}; use std::{ collections::HashMap, diff --git a/lib/wasix/src/state/func_env.rs b/lib/wasix/src/state/func_env.rs index 845c55a3ef3..e1348167fbe 100644 --- a/lib/wasix/src/state/func_env.rs +++ b/lib/wasix/src/state/func_env.rs @@ -8,9 +8,10 @@ use wasmer_wasix_types::wasi::ExitCode; #[cfg(feature = "journal")] use crate::syscalls::restore_snapshot; +#[allow(unused_imports)] +use crate::os::task::thread::RewindResultType; use crate::{ import_object_for_all_wasi_versions, - os::task::thread::RewindResultType, runtime::SpawnMemoryType, state::WasiInstanceHandles, utils::{get_wasi_version, get_wasi_versions, store::restore_store_snapshot}, diff --git a/lib/wasix/src/syscalls/journal/mod.rs b/lib/wasix/src/syscalls/journal/mod.rs index 571b11dd903..984c9b5b686 100644 --- a/lib/wasix/src/syscalls/journal/mod.rs +++ b/lib/wasix/src/syscalls/journal/mod.rs @@ -1,13 +1,16 @@ +#[cfg(feature = "journal")] mod actions; mod clear_ethereal; mod do_checkpoint_from_outside; mod maybe_snapshot; mod maybe_snapshot_many; mod maybe_snapshot_once; +#[cfg(feature = "journal")] mod play_event; mod restore_snapshot; mod wait_for_snapshot; +#[cfg(feature = "journal")] use actions::*; use clear_ethereal::*; use wasmer_journal::JournalEntry; diff --git a/lib/wasix/src/syscalls/mod.rs b/lib/wasix/src/syscalls/mod.rs index 4d384e355db..1db60dc8c6b 100644 --- a/lib/wasix/src/syscalls/mod.rs +++ b/lib/wasix/src/syscalls/mod.rs @@ -1009,19 +1009,22 @@ pub(crate) fn deep_sleep( store_data.len(), ); - // Write our thread state to the snapshot - let tid = ctx.data().thread.tid(); - let thread_start = ctx.data().thread.thread_start_type(); - if let Err(err) = JournalEffector::save_thread_state::( - &mut ctx, - tid, - memory_stack.clone(), - rewind_stack.clone(), - store_data.clone(), - thread_start, - thread_layout.clone(), - ) { - return wasmer_types::OnCalledAction::Trap(err.into()); + #[cfg(feature = "journal")] + { + // Write our thread state to the snapshot + let tid = ctx.data().thread.tid(); + let thread_start = ctx.data().thread.thread_start_type(); + if let Err(err) = JournalEffector::save_thread_state::( + &mut ctx, + tid, + memory_stack.clone(), + rewind_stack.clone(), + store_data.clone(), + thread_start, + thread_layout.clone(), + ) { + return wasmer_types::OnCalledAction::Trap(err.into()); + } } // If all the threads are now in a deep sleep state @@ -1031,15 +1034,22 @@ pub(crate) fn deep_sleep( let mut guard = inner.0.lock().unwrap(); guard.threads.values().all(WasiThread::is_deep_sleeping) }; - if is_idle { - if ctx.data_mut().has_snapshot_trigger(SnapshotTrigger::Idle) { - let mut guard = inner.0.lock().unwrap(); - if let Err(err) = JournalEffector::save_memory_and_snapshot( - &mut ctx, - &mut guard, - SnapshotTrigger::Idle, - ) { - return wasmer_types::OnCalledAction::Trap(err.into()); + + // When we idle the journal functionality may be set + // will take a snapshot of the memory and threads so + // that it can resumed. + #[cfg(feature = "journal")] + { + if is_idle { + if ctx.data_mut().has_snapshot_trigger(SnapshotTrigger::Idle) { + let mut guard = inner.0.lock().unwrap(); + if let Err(err) = JournalEffector::save_memory_and_snapshot( + &mut ctx, + &mut guard, + SnapshotTrigger::Idle, + ) { + return wasmer_types::OnCalledAction::Trap(err.into()); + } } } } From 573b3422daf46598fa793f30202072319cd4857f Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 11:14:44 +1100 Subject: [PATCH 56/67] Linting fix --- lib/wasix/src/state/func_env.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/wasix/src/state/func_env.rs b/lib/wasix/src/state/func_env.rs index e1348167fbe..001f3290f94 100644 --- a/lib/wasix/src/state/func_env.rs +++ b/lib/wasix/src/state/func_env.rs @@ -6,10 +6,10 @@ use wasmer::{ }; use wasmer_wasix_types::wasi::ExitCode; -#[cfg(feature = "journal")] -use crate::syscalls::restore_snapshot; #[allow(unused_imports)] use crate::os::task::thread::RewindResultType; +#[cfg(feature = "journal")] +use crate::syscalls::restore_snapshot; use crate::{ import_object_for_all_wasi_versions, runtime::SpawnMemoryType, From 502957fae46d67dc89443730fd6de92669f8c15b Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 11:24:56 +1100 Subject: [PATCH 57/67] More minor linting fixes --- lib/virtual-net/src/loopback.rs | 2 +- lib/virtual-net/src/tcp_pair.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/virtual-net/src/loopback.rs b/lib/virtual-net/src/loopback.rs index f6da120fd7e..93a68b10ad0 100644 --- a/lib/virtual-net/src/loopback.rs +++ b/lib/virtual-net/src/loopback.rs @@ -189,7 +189,7 @@ impl VirtualIoSource for LoopbackTcpListener { if !state.backlog.is_empty() { return Poll::Ready(Ok(state.backlog.len())); } - if state.wakers.iter().any(|w| w.will_wake(cx.waker())) == false { + if !state.wakers.iter().any(|w| w.will_wake(cx.waker())) { state.wakers.push(cx.waker().clone()); } Poll::Pending diff --git a/lib/virtual-net/src/tcp_pair.rs b/lib/virtual-net/src/tcp_pair.rs index 54528000746..567d4c1db03 100644 --- a/lib/virtual-net/src/tcp_pair.rs +++ b/lib/virtual-net/src/tcp_pair.rs @@ -28,7 +28,7 @@ struct SocketBufferState { impl SocketBufferState { fn add_waker(&mut self, waker: &Waker) { - if self.wakers.iter().any(|w| w.will_wake(waker)) == false { + if !self.wakers.iter().any(|w| w.will_wake(waker)) { self.wakers.push(waker.clone()); } } @@ -103,7 +103,7 @@ impl SocketBuffer { if state.dead { return Poll::Ready(Ok(0)); } - if state.wakers.iter().any(|w| w.will_wake(cx.waker())) == false { + if !state.wakers.iter().any(|w| w.will_wake(cx.waker())) { state.wakers.push(cx.waker().clone()); } Poll::Pending @@ -114,11 +114,11 @@ impl SocketBuffer { if state.dead { return Poll::Ready(Ok(0)); } - if !state.buffer.is_full() && state.halt_immediate_poll_write == false { + if !state.buffer.is_full() && !state.halt_immediate_poll_write { state.halt_immediate_poll_write = true; return Poll::Ready(Ok(state.buffer.window())); } - if state.wakers.iter().any(|w| w.will_wake(cx.waker())) == false { + if !state.wakers.iter().any(|w| w.will_wake(cx.waker())) { state.wakers.push(cx.waker().clone()); } Poll::Pending @@ -334,7 +334,7 @@ impl TcpSocketHalf { } pub fn is_active(&self) -> bool { - self.tx.is_dead() == false + !self.tx.is_dead() } pub fn close(&self) -> crate::Result<()> { From 0b04236834074d17910e7e0445fa4d3741452a72 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 11:36:35 +1100 Subject: [PATCH 58/67] Removed fuse as a default dependency as it does not work on all platforms --- lib/cli/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index ea879223fa4..72b79789426 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -34,7 +34,6 @@ default = [ "wast", "compiler", "journal", - "fuse", "wasmer-artifact-create", "static-artifact-create", ] From 51e2e752ed3bead63673f1407244b19c68320638 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 11:49:49 +1100 Subject: [PATCH 59/67] More linting fixes --- lib/journal/src/concrete/aligned_cow_str.rs | 6 ++++++ lib/journal/src/concrete/aligned_cow_vec.rs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/lib/journal/src/concrete/aligned_cow_str.rs b/lib/journal/src/concrete/aligned_cow_str.rs index 36a2d7bdef8..c9676cbaf49 100644 --- a/lib/journal/src/concrete/aligned_cow_str.rs +++ b/lib/journal/src/concrete/aligned_cow_str.rs @@ -26,6 +26,10 @@ impl<'a> AlignedCowStr<'a> { pub fn len(&self) -> usize { self.inner.len() } + + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } } impl<'a> Default for AlignedCowStr<'a> { @@ -51,6 +55,7 @@ impl<'a> From for AlignedCowStr<'a> { } } +#[allow(clippy::from_over_into)] impl<'a> Into for AlignedCowStr<'a> { fn into(self) -> String { self.inner.into_owned() @@ -63,6 +68,7 @@ impl<'a> From> for AlignedCowStr<'a> { } } +#[allow(clippy::from_over_into)] impl<'a> Into> for AlignedCowStr<'a> { fn into(self) -> Cow<'a, str> { self.inner diff --git a/lib/journal/src/concrete/aligned_cow_vec.rs b/lib/journal/src/concrete/aligned_cow_vec.rs index eebfde6e7e6..49ff1bb93c7 100644 --- a/lib/journal/src/concrete/aligned_cow_vec.rs +++ b/lib/journal/src/concrete/aligned_cow_vec.rs @@ -42,6 +42,10 @@ where self.inner.len() } + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + pub fn len_with_padding(&self) -> usize { let mut ret = self.inner.len() * std::mem::size_of::(); let padding = ret % Self::ALIGNMENT; @@ -87,6 +91,7 @@ where } } +#[allow(clippy::from_over_into)] impl<'a> Into> for AlignedCowVec<'a, u8> { fn into(self) -> Vec { self.inner.into_owned() @@ -103,6 +108,7 @@ where } } +#[allow(clippy::from_over_into)] impl<'a, T> Into> for AlignedCowVec<'a, T> where T: 'a, From fc85d902ea126cda7d673c2bbe4d2bf19e07b4ff Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 17:56:29 +1100 Subject: [PATCH 60/67] More linting fixes --- lib/wasi-types/src/wasix/mod.rs | 10 +++++++--- lib/wasix/src/fs/mod.rs | 2 +- .../journal/effector/memory_and_snapshot.rs | 2 +- .../effector/syscalls/path_create_directory.rs | 2 +- .../effector/syscalls/path_remove_directory.rs | 14 ++++++-------- .../journal/effector/syscalls/path_unlink.rs | 2 +- lib/wasix/src/os/task/process.rs | 13 +++++++------ lib/wasix/src/os/task/thread.rs | 2 +- .../syscalls/journal/actions/close_thread.rs | 1 + .../src/syscalls/journal/actions/fd_advise.rs | 1 + .../syscalls/journal/actions/fd_allocate.rs | 1 + .../src/syscalls/journal/actions/fd_close.rs | 1 + .../src/syscalls/journal/actions/fd_dup.rs | 1 + .../src/syscalls/journal/actions/fd_open.rs | 1 + .../syscalls/journal/actions/fd_renumber.rs | 1 + .../src/syscalls/journal/actions/fd_seek.rs | 1 + .../syscalls/journal/actions/fd_set_flags.rs | 1 + .../syscalls/journal/actions/fd_set_rights.rs | 1 + .../syscalls/journal/actions/fd_set_size.rs | 1 + .../syscalls/journal/actions/fd_set_times.rs | 1 + .../src/syscalls/journal/actions/fd_write.rs | 1 + .../syscalls/journal/actions/path_set_times.rs | 1 + .../syscalls/journal/actions/process_exit.rs | 1 + .../src/syscalls/journal/actions/set_thread.rs | 1 + .../src/syscalls/journal/actions/snapshot.rs | 1 + .../src/syscalls/journal/actions/tty_set.rs | 1 + .../syscalls/journal/actions/update_memory.rs | 1 + .../src/syscalls/journal/maybe_snapshot.rs | 12 ++++++------ .../syscalls/journal/maybe_snapshot_many.rs | 12 ++++++------ .../syscalls/journal/maybe_snapshot_once.rs | 12 ++++++------ lib/wasix/src/syscalls/journal/play_event.rs | 1 + lib/wasix/src/syscalls/mod.rs | 18 ++++++++---------- 32 files changed, 71 insertions(+), 50 deletions(-) diff --git a/lib/wasi-types/src/wasix/mod.rs b/lib/wasi-types/src/wasix/mod.rs index c9a319a607b..8f601ed578b 100644 --- a/lib/wasi-types/src/wasix/mod.rs +++ b/lib/wasi-types/src/wasix/mod.rs @@ -1,16 +1,20 @@ +#[cfg(feature = "enable-serde")] use serde::*; // pub mod wasix_http_client_v1; -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialOrd, Ord, PartialEq, Eq, Hash)] -#[serde(rename_all = "snake_case")] +#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "enable-serde", serde(rename_all = "snake_case"))] pub enum ThreadStartType { MainThread, ThreadSpawn { start_ptr: u64 }, } /// Represents the memory layout of the parts that the thread itself uses -#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "enable-serde", serde(rename_all = "snake_case"))] pub struct WasiMemoryLayout { /// This is the top part of the stack (stacks go backwards) pub stack_upper: u64, diff --git a/lib/wasix/src/fs/mod.rs b/lib/wasix/src/fs/mod.rs index ed5d837d13d..c10420cf086 100644 --- a/lib/wasix/src/fs/mod.rs +++ b/lib/wasix/src/fs/mod.rs @@ -1106,7 +1106,7 @@ impl WasiFs { if let Some(entry) = entries.get(component.as_ref()) { cur_inode = entry.clone(); - } else if let Some(root) = entries.get(&format!("/")) { + } else if let Some(root) = entries.get(&"/".to_string()) { cur_inode = root.clone(); continue 'symlink_resolution; } else { diff --git a/lib/wasix/src/journal/effector/memory_and_snapshot.rs b/lib/wasix/src/journal/effector/memory_and_snapshot.rs index 860905da2cd..145f5ce4419 100644 --- a/lib/wasix/src/journal/effector/memory_and_snapshot.rs +++ b/lib/wasix/src/journal/effector/memory_and_snapshot.rs @@ -113,7 +113,7 @@ impl JournalEffector { // Compute a checksum and skip the memory if its already // been saved to the journal once already let hash = { - let h: [u8; 32] = blake3::hash(&data).into(); + let h: [u8; 32] = blake3::hash(data).into(); u64::from_be_bytes([h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]]) }; match guard.snapshot_memory_hash.entry(region) { diff --git a/lib/wasix/src/journal/effector/syscalls/path_create_directory.rs b/lib/wasix/src/journal/effector/syscalls/path_create_directory.rs index c44f7f32083..223bce3fd30 100644 --- a/lib/wasix/src/journal/effector/syscalls/path_create_directory.rs +++ b/lib/wasix/src/journal/effector/syscalls/path_create_directory.rs @@ -28,7 +28,7 @@ impl JournalEffector { ) -> anyhow::Result<()> { // see `VIRTUAL_ROOT_FD` for details as to why this exists if fd == VIRTUAL_ROOT_FD { - ctx.data().state.fs.root_fs.create_dir(&Path::new(path))?; + ctx.data().state.fs.root_fs.create_dir(Path::new(path))?; } else { crate::syscalls::path_create_directory_internal(ctx, fd, path).map_err(|err| { anyhow::format_err!( diff --git a/lib/wasix/src/journal/effector/syscalls/path_remove_directory.rs b/lib/wasix/src/journal/effector/syscalls/path_remove_directory.rs index 0ef9974e1b1..f26fb5d99f1 100644 --- a/lib/wasix/src/journal/effector/syscalls/path_remove_directory.rs +++ b/lib/wasix/src/journal/effector/syscalls/path_remove_directory.rs @@ -28,14 +28,12 @@ impl JournalEffector { ) -> anyhow::Result<()> { // see `VIRTUAL_ROOT_FD` for details as to why this exists if fd == VIRTUAL_ROOT_FD { - ctx.data().state.fs.root_fs.remove_dir(&Path::new(path))?; - } else { - if let Err(err) = crate::syscalls::path_remove_directory_internal(ctx, fd, path) { - bail!( - "journal restore error: failed to remove directory - {}", - err - ); - } + ctx.data().state.fs.root_fs.remove_dir(Path::new(path))?; + } else if let Err(err) = crate::syscalls::path_remove_directory_internal(ctx, fd, path) { + bail!( + "journal restore error: failed to remove directory - {}", + err + ); } Ok(()) } diff --git a/lib/wasix/src/journal/effector/syscalls/path_unlink.rs b/lib/wasix/src/journal/effector/syscalls/path_unlink.rs index f155f741fa0..035d2e905bf 100644 --- a/lib/wasix/src/journal/effector/syscalls/path_unlink.rs +++ b/lib/wasix/src/journal/effector/syscalls/path_unlink.rs @@ -28,7 +28,7 @@ impl JournalEffector { ) -> anyhow::Result<()> { // see `VIRTUAL_ROOT_FD` for details as to why this exists if fd == VIRTUAL_ROOT_FD { - ctx.data().state.fs.root_fs.remove_file(&Path::new(path))?; + ctx.data().state.fs.root_fs.remove_file(Path::new(path))?; } else { let ret = crate::syscalls::path_unlink_file_internal(ctx, fd, path)?; if ret != Errno::Success { diff --git a/lib/wasix/src/os/task/process.rs b/lib/wasix/src/os/task/process.rs index 2c0a3b042a2..f165033755f 100644 --- a/lib/wasix/src/os/task/process.rs +++ b/lib/wasix/src/os/task/process.rs @@ -139,6 +139,7 @@ impl From> for MemorySnapshotRegion { } } +#[allow(clippy::from_over_into)] impl Into> for MemorySnapshotRegion { fn into(self) -> Range { self.start..self.end @@ -204,10 +205,10 @@ impl WasiProcessInner { /// If a checkpoint has been started this will block the current process /// until the checkpoint operation has completed #[cfg(feature = "journal")] - pub fn maybe_checkpoint<'a, M: wasmer_types::MemorySize>( + pub fn maybe_checkpoint( inner: LockableWasiProcessInner, - ctx: FunctionEnvMut<'a, WasiEnv>, - ) -> WasiResult> { + ctx: FunctionEnvMut<'_, WasiEnv>, + ) -> WasiResult> { // Enter the lock which will determine if we are in a checkpoint or not use bytes::Bytes; @@ -319,11 +320,11 @@ impl WasiProcessInner { // Execute any checkpoints that can be executed while outside of the WASM process #[cfg(not(feature = "journal"))] - pub fn do_checkpoints_from_outside<'a>(_ctx: &mut FunctionEnvMut<'a, WasiEnv>) {} + pub fn do_checkpoints_from_outside(_ctx: &mut FunctionEnvMut<'_, WasiEnv>) {} // Execute any checkpoints that can be executed while outside of the WASM process #[cfg(feature = "journal")] - pub fn do_checkpoints_from_outside<'a>(mut ctx: &mut FunctionEnvMut<'a, WasiEnv>) { + pub fn do_checkpoints_from_outside(ctx: &mut FunctionEnvMut<'_, WasiEnv>) { let inner = ctx.data().process.inner.clone(); let mut guard = inner.0.lock().unwrap(); @@ -339,7 +340,7 @@ impl WasiProcessInner { .all(|t| t.is_check_pointing() || t.is_deep_sleeping()); if is_last_thread { if let Err(err) = - JournalEffector::save_memory_and_snapshot(&mut ctx, &mut guard, trigger) + JournalEffector::save_memory_and_snapshot(ctx, &mut guard, trigger) { inner.1.notify_all(); tracing::error!("failed to snapshot memory and threads - {}", err); diff --git a/lib/wasix/src/os/task/thread.rs b/lib/wasix/src/os/task/thread.rs index 327844d1f3e..cdb3ec4420a 100644 --- a/lib/wasix/src/os/task/thread.rs +++ b/lib/wasix/src/os/task/thread.rs @@ -119,7 +119,7 @@ impl WasiThread { /// Gets the thread start type for this thread pub fn thread_start_type(&self) -> ThreadStartType { - self.start.clone() + self.start } /// Returns true if a rewind of a particular type has been queued diff --git a/lib/wasix/src/syscalls/journal/actions/close_thread.rs b/lib/wasix/src/syscalls/journal/actions/close_thread.rs index cefdff780e6..a2fa0b8b0c9 100644 --- a/lib/wasix/src/syscalls/journal/actions/close_thread.rs +++ b/lib/wasix/src/syscalls/journal/actions/close_thread.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_close_thread( &mut self, id: u32, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_advise.rs b/lib/wasix/src/syscalls/journal/actions/fd_advise.rs index a888705ec0f..2e094bc9e7f 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_advise.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_advise.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_fd_advise( &mut self, fd: Fd, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_allocate.rs b/lib/wasix/src/syscalls/journal/actions/fd_allocate.rs index 8f7125bcd49..dbf1896003d 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_allocate.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_allocate.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_fd_allocate( &mut self, fd: Fd, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_close.rs b/lib/wasix/src/syscalls/journal/actions/fd_close.rs index 1cb5df47f29..26b308c5aad 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_close.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_close.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_fd_close(&mut self, fd: u32) -> Result<(), WasiRuntimeError> { tracing::trace!(%fd, "Replay journal - FdClose"); self.stdout_fds.remove(&fd); diff --git a/lib/wasix/src/syscalls/journal/actions/fd_dup.rs b/lib/wasix/src/syscalls/journal/actions/fd_dup.rs index cc1fd2d5a63..dd4bd8d3baf 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_dup.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_dup.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_fd_dup( &mut self, original_fd: u32, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_open.rs b/lib/wasix/src/syscalls/journal/actions/fd_open.rs index 64dc09ff89f..2393bac14a0 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_open.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_open.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_fd_open( &mut self, fd: u32, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_renumber.rs b/lib/wasix/src/syscalls/journal/actions/fd_renumber.rs index 8656dd0772b..b2b485f075c 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_renumber.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_renumber.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_fd_renumber( &mut self, old_fd: u32, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_seek.rs b/lib/wasix/src/syscalls/journal/actions/fd_seek.rs index f44a7a19287..c57b10dd3ec 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_seek.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_seek.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_fd_seek( &mut self, fd: u32, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_set_flags.rs b/lib/wasix/src/syscalls/journal/actions/fd_set_flags.rs index 525a63b62d6..db2511cf9b6 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_set_flags.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_set_flags.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_fd_set_flags( &mut self, fd: Fd, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_set_rights.rs b/lib/wasix/src/syscalls/journal/actions/fd_set_rights.rs index 1ea0e6f34b4..479fe235241 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_set_rights.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_set_rights.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_fd_set_rights( &mut self, fd: Fd, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_set_size.rs b/lib/wasix/src/syscalls/journal/actions/fd_set_size.rs index d36fed586ce..12084fc969e 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_set_size.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_set_size.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_fd_set_size( &mut self, fd: Fd, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_set_times.rs b/lib/wasix/src/syscalls/journal/actions/fd_set_times.rs index 1cd0bf3c4ee..7c615e9e23b 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_set_times.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_set_times.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_fd_set_times( &mut self, fd: Fd, diff --git a/lib/wasix/src/syscalls/journal/actions/fd_write.rs b/lib/wasix/src/syscalls/journal/actions/fd_write.rs index 590d6caf2b3..06e7249d85e 100644 --- a/lib/wasix/src/syscalls/journal/actions/fd_write.rs +++ b/lib/wasix/src/syscalls/journal/actions/fd_write.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_fd_write( &mut self, fd: u32, diff --git a/lib/wasix/src/syscalls/journal/actions/path_set_times.rs b/lib/wasix/src/syscalls/journal/actions/path_set_times.rs index d191f89e319..c91fe3c21ee 100644 --- a/lib/wasix/src/syscalls/journal/actions/path_set_times.rs +++ b/lib/wasix/src/syscalls/journal/actions/path_set_times.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_path_set_times( &mut self, fd: Fd, diff --git a/lib/wasix/src/syscalls/journal/actions/process_exit.rs b/lib/wasix/src/syscalls/journal/actions/process_exit.rs index 0089c3935f9..6228c88658e 100644 --- a/lib/wasix/src/syscalls/journal/actions/process_exit.rs +++ b/lib/wasix/src/syscalls/journal/actions/process_exit.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_process_exit( &mut self, exit_code: Option, diff --git a/lib/wasix/src/syscalls/journal/actions/set_thread.rs b/lib/wasix/src/syscalls/journal/actions/set_thread.rs index 854c648036e..397b5a7f99a 100644 --- a/lib/wasix/src/syscalls/journal/actions/set_thread.rs +++ b/lib/wasix/src/syscalls/journal/actions/set_thread.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_set_thread( &mut self, id: u32, diff --git a/lib/wasix/src/syscalls/journal/actions/snapshot.rs b/lib/wasix/src/syscalls/journal/actions/snapshot.rs index 253f4174a5f..18e004d47db 100644 --- a/lib/wasix/src/syscalls/journal/actions/snapshot.rs +++ b/lib/wasix/src/syscalls/journal/actions/snapshot.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_snapshot( &mut self, when: SystemTime, diff --git a/lib/wasix/src/syscalls/journal/actions/tty_set.rs b/lib/wasix/src/syscalls/journal/actions/tty_set.rs index aa7b85a00cb..2d0206e3942 100644 --- a/lib/wasix/src/syscalls/journal/actions/tty_set.rs +++ b/lib/wasix/src/syscalls/journal/actions/tty_set.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_tty_set( &mut self, tty: Tty, diff --git a/lib/wasix/src/syscalls/journal/actions/update_memory.rs b/lib/wasix/src/syscalls/journal/actions/update_memory.rs index 5c5be23073a..f04b36cc47c 100644 --- a/lib/wasix/src/syscalls/journal/actions/update_memory.rs +++ b/lib/wasix/src/syscalls/journal/actions/update_memory.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_update_memory( &mut self, region: Range, diff --git a/lib/wasix/src/syscalls/journal/maybe_snapshot.rs b/lib/wasix/src/syscalls/journal/maybe_snapshot.rs index 1dd7bdd2fc0..83fd7faf34a 100644 --- a/lib/wasix/src/syscalls/journal/maybe_snapshot.rs +++ b/lib/wasix/src/syscalls/journal/maybe_snapshot.rs @@ -2,16 +2,16 @@ use super::*; #[allow(clippy::extra_unused_type_parameters)] #[cfg(not(feature = "journal"))] -pub fn maybe_snapshot<'a, M: MemorySize>( - ctx: FunctionEnvMut<'a, WasiEnv>, -) -> WasiResult> { +pub fn maybe_snapshot( + ctx: FunctionEnvMut<'_, WasiEnv>, +) -> WasiResult> { Ok(Ok(ctx)) } #[cfg(feature = "journal")] -pub fn maybe_snapshot<'a, M: MemorySize>( - mut ctx: FunctionEnvMut<'a, WasiEnv>, -) -> WasiResult> { +pub fn maybe_snapshot( + mut ctx: FunctionEnvMut<'_, WasiEnv>, +) -> WasiResult> { use crate::os::task::process::{WasiProcessCheckpoint, WasiProcessInner}; if !ctx.data().enable_journal { diff --git a/lib/wasix/src/syscalls/journal/maybe_snapshot_many.rs b/lib/wasix/src/syscalls/journal/maybe_snapshot_many.rs index ec032978912..866d8999d91 100644 --- a/lib/wasix/src/syscalls/journal/maybe_snapshot_many.rs +++ b/lib/wasix/src/syscalls/journal/maybe_snapshot_many.rs @@ -2,18 +2,18 @@ use super::*; #[allow(clippy::extra_unused_type_parameters)] #[cfg(not(feature = "journal"))] -pub fn maybe_snapshot_many<'a, M: MemorySize>( - ctx: FunctionEnvMut<'a, WasiEnv>, +pub fn maybe_snapshot_many( + ctx: FunctionEnvMut<'_, WasiEnv>, _trigger: crate::journal::SnapshotTrigger, -) -> WasiResult> { +) -> WasiResult> { Ok(Ok(ctx)) } #[cfg(feature = "journal")] -pub fn maybe_snapshot_many<'a, M: MemorySize>( - mut ctx: FunctionEnvMut<'a, WasiEnv>, +pub fn maybe_snapshot_many( + mut ctx: FunctionEnvMut<'_, WasiEnv>, trigger: crate::journal::SnapshotTrigger, -) -> WasiResult> { +) -> WasiResult> { use crate::os::task::process::{WasiProcessCheckpoint, WasiProcessInner}; if unsafe { handle_rewind_ext_with_default::(&mut ctx, HandleRewindType::ResultLess) } diff --git a/lib/wasix/src/syscalls/journal/maybe_snapshot_once.rs b/lib/wasix/src/syscalls/journal/maybe_snapshot_once.rs index 45399730036..8938f63de3e 100644 --- a/lib/wasix/src/syscalls/journal/maybe_snapshot_once.rs +++ b/lib/wasix/src/syscalls/journal/maybe_snapshot_once.rs @@ -2,18 +2,18 @@ use super::*; #[allow(clippy::extra_unused_type_parameters)] #[cfg(not(feature = "journal"))] -pub fn maybe_snapshot_once<'a, M: MemorySize>( - ctx: FunctionEnvMut<'a, WasiEnv>, +pub fn maybe_snapshot_once( + ctx: FunctionEnvMut<'_, WasiEnv>, _trigger: crate::journal::SnapshotTrigger, -) -> WasiResult> { +) -> WasiResult> { Ok(Ok(ctx)) } #[cfg(feature = "journal")] -pub fn maybe_snapshot_once<'a, M: MemorySize>( - mut ctx: FunctionEnvMut<'a, WasiEnv>, +pub fn maybe_snapshot_once( + mut ctx: FunctionEnvMut<'_, WasiEnv>, trigger: crate::journal::SnapshotTrigger, -) -> WasiResult> { +) -> WasiResult> { use crate::os::task::process::{WasiProcessCheckpoint, WasiProcessInner}; if unsafe { handle_rewind_ext_with_default::(&mut ctx, HandleRewindType::ResultLess) } diff --git a/lib/wasix/src/syscalls/journal/play_event.rs b/lib/wasix/src/syscalls/journal/play_event.rs index e9cb1ddc15a..674d078633f 100644 --- a/lib/wasix/src/syscalls/journal/play_event.rs +++ b/lib/wasix/src/syscalls/journal/play_event.rs @@ -3,6 +3,7 @@ use std::ops::Range; use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(super) unsafe fn play_event( &mut self, next: JournalEntry<'a>, diff --git a/lib/wasix/src/syscalls/mod.rs b/lib/wasix/src/syscalls/mod.rs index 1db60dc8c6b..9857549d22e 100644 --- a/lib/wasix/src/syscalls/mod.rs +++ b/lib/wasix/src/syscalls/mod.rs @@ -1040,16 +1040,14 @@ pub(crate) fn deep_sleep( // that it can resumed. #[cfg(feature = "journal")] { - if is_idle { - if ctx.data_mut().has_snapshot_trigger(SnapshotTrigger::Idle) { - let mut guard = inner.0.lock().unwrap(); - if let Err(err) = JournalEffector::save_memory_and_snapshot( - &mut ctx, - &mut guard, - SnapshotTrigger::Idle, - ) { - return wasmer_types::OnCalledAction::Trap(err.into()); - } + if is_idle && ctx.data_mut().has_snapshot_trigger(SnapshotTrigger::Idle) { + let mut guard = inner.0.lock().unwrap(); + if let Err(err) = JournalEffector::save_memory_and_snapshot( + &mut ctx, + &mut guard, + SnapshotTrigger::Idle, + ) { + return wasmer_types::OnCalledAction::Trap(err.into()); } } } From 62b7c7215af9610ef3b293cb9ffc4087016fa804 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 18:00:14 +1100 Subject: [PATCH 61/67] Fixed some unit tests --- lib/journal/src/concrete/log_file.rs | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/journal/src/concrete/log_file.rs b/lib/journal/src/concrete/log_file.rs index 513a0bf9402..19a7d7ea494 100644 --- a/lib/journal/src/concrete/log_file.rs +++ b/lib/journal/src/concrete/log_file.rs @@ -281,6 +281,8 @@ impl Journal for LogFileJournal { #[cfg(test)] mod tests { + use wasmer_wasix_types::wasix::WasiMemoryLayout; + use super::*; #[tracing_test::traced_test] @@ -301,6 +303,13 @@ mod tests { memory_stack: vec![22; 16].into(), store_data: vec![33; 136].into(), is_64bit: false, + layout: WasiMemoryLayout { + stack_upper: 0, + stack_lower: 1024, + guard_size: 16, + stack_size: 1024, + }, + start: wasmer_wasix_types::wasix::ThreadStartType::MainThread, }) .unwrap(); journal.write(JournalEntry::PortAddrClearV1).unwrap(); @@ -323,6 +332,13 @@ mod tests { memory_stack: vec![22; 16].into(), store_data: vec![33; 136].into(), is_64bit: false, + layout: WasiMemoryLayout { + stack_upper: 0, + stack_lower: 1024, + guard_size: 16, + stack_size: 1024, + }, + start: wasmer_wasix_types::wasix::ThreadStartType::MainThread, }) ); assert_eq!(event3, Some(JournalEntry::PortAddrClearV1)); @@ -366,6 +382,13 @@ mod tests { memory_stack: vec![22; 16].into(), store_data: vec![33; 136].into(), is_64bit: false, + layout: WasiMemoryLayout { + stack_upper: 0, + stack_lower: 1024, + guard_size: 16, + stack_size: 1024, + }, + start: wasmer_wasix_types::wasix::ThreadStartType::MainThread, }) ); assert_eq!(event3, Some(JournalEntry::PortAddrClearV1)); @@ -406,6 +429,13 @@ mod tests { memory_stack: vec![22; 16].into(), store_data: vec![33; 136].into(), is_64bit: false, + layout: WasiMemoryLayout { + stack_upper: 0, + stack_lower: 1024, + guard_size: 16, + stack_size: 1024, + }, + start: wasmer_wasix_types::wasix::ThreadStartType::MainThread, }) ); assert_eq!(event3, Some(JournalEntry::PortAddrClearV1)); From 0a6abfd1b0ee9067c707bb1d1a3457b0c82d85f6 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 18:01:52 +1100 Subject: [PATCH 62/67] Fixed some unit tests --- lib/journal/src/concrete/tests.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/journal/src/concrete/tests.rs b/lib/journal/src/concrete/tests.rs index a987069d21e..faebfcc296d 100644 --- a/lib/journal/src/concrete/tests.rs +++ b/lib/journal/src/concrete/tests.rs @@ -70,6 +70,13 @@ pub fn test_record_set_thread() { memory_stack: vec![4, 5, 6, 7].into(), store_data: vec![10, 11].into(), is_64bit: true, + layout: wasmer_wasix_types::wasix::WasiMemoryLayout { + stack_upper: 0, + stack_lower: 1024, + guard_size: 16, + stack_size: 1024, + }, + start: wasmer_wasix_types::wasix::ThreadStartType::MainThread, }); } From 329f9729c5d60bd675cdaa4faf4f810e4fc256fd Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 18:15:03 +1100 Subject: [PATCH 63/67] Fixed one of the lints that was missed --- lib/wasix/src/syscalls/journal/actions/init_module.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/wasix/src/syscalls/journal/actions/init_module.rs b/lib/wasix/src/syscalls/journal/actions/init_module.rs index d8f476bab00..2e0dfe59a15 100644 --- a/lib/wasix/src/syscalls/journal/actions/init_module.rs +++ b/lib/wasix/src/syscalls/journal/actions/init_module.rs @@ -1,6 +1,7 @@ use super::*; impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { + #[allow(clippy::result_large_err)] pub(crate) unsafe fn action_init_module( &mut self, wasm_hash: [u8; 8], From de476a6b3d1d9c0aed22cb180ea0cfe5a0754c1d Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 19:12:20 +1100 Subject: [PATCH 64/67] Linting and unit test fixes --- lib/virtual-fs/src/mem_fs/offloaded_file.rs | 5 +++-- lib/wasix/src/runners/dproxy/handler.rs | 2 +- lib/wasix/src/runners/dproxy/networking.rs | 2 +- lib/wasix/src/runners/dproxy/socket_manager.rs | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/virtual-fs/src/mem_fs/offloaded_file.rs b/lib/virtual-fs/src/mem_fs/offloaded_file.rs index 049bd4e0644..a4308be32bd 100644 --- a/lib/virtual-fs/src/mem_fs/offloaded_file.rs +++ b/lib/virtual-fs/src/mem_fs/offloaded_file.rs @@ -231,7 +231,8 @@ impl OffloadedFile { } pub fn write(&mut self, data: OffloadWrite<'_>, cursor: &mut u64) -> io::Result { - let mut extent_offset = *cursor; + let original_extent_offset = *cursor; + let mut extent_offset = original_extent_offset; let mut data_len = data.len() as u64; // We need to split any extents that are intersecting with the @@ -348,7 +349,7 @@ impl OffloadedFile { self.extents.insert(index, new_extent); } } - self.size = extent_offset + data_len; + self.size = self.size.max(original_extent_offset + data.len() as u64); // Update the cursor *cursor += data.len() as u64; diff --git a/lib/wasix/src/runners/dproxy/handler.rs b/lib/wasix/src/runners/dproxy/handler.rs index 9b03da53946..3d1eb035404 100644 --- a/lib/wasix/src/runners/dproxy/handler.rs +++ b/lib/wasix/src/runners/dproxy/handler.rs @@ -58,7 +58,7 @@ impl Handler { .headers() .get("X-Shard") .map(|v| String::from_utf8_lossy(v.as_bytes())) - .map(|s| match u64::from_str_radix(&s, 10) { + .map(|s| match (&s).parse::() { Ok(id) => Ok(Shard::ById(id)), Err(err) => Err(err), }) diff --git a/lib/wasix/src/runners/dproxy/networking.rs b/lib/wasix/src/runners/dproxy/networking.rs index 9e7baf2915a..8e46f1ce526 100644 --- a/lib/wasix/src/runners/dproxy/networking.rs +++ b/lib/wasix/src/runners/dproxy/networking.rs @@ -43,7 +43,7 @@ impl LocalWithLoopbackNetworking { return Poll::Ready(*addr); } - if listening.wakers.iter().any(|w| w.will_wake(cx.waker())) == false { + if !listening.wakers.iter().any(|w| w.will_wake(cx.waker())) { listening.wakers.push(cx.waker().clone()); } diff --git a/lib/wasix/src/runners/dproxy/socket_manager.rs b/lib/wasix/src/runners/dproxy/socket_manager.rs index 9198a35e626..658832fde1a 100644 --- a/lib/wasix/src/runners/dproxy/socket_manager.rs +++ b/lib/wasix/src/runners/dproxy/socket_manager.rs @@ -64,7 +64,7 @@ impl SocketManager { "failed to open HTTP socket as the instance has terminated" )); } - let connect_timeout = if self.is_running.load(Ordering::SeqCst) == true { + let connect_timeout = if self.is_running.load(Ordering::SeqCst) { self.proxy_connect_nominal_timeout } else { self.proxy_connect_init_timeout From 46914b9b3c1aad72b45e45b3c84877656c9e9dfc Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 20:03:21 +1100 Subject: [PATCH 65/67] Fixed multithreading for journals --- .../syscalls/journal/actions/close_thread.rs | 4 ++- .../syscalls/journal/actions/set_thread.rs | 7 +++- .../src/syscalls/journal/actions/snapshot.rs | 15 +++++--- .../syscalls/journal/actions/update_memory.rs | 5 ++- .../src/syscalls/journal/clear_ethereal.rs | 1 + lib/wasix/src/syscalls/journal/play_event.rs | 34 +++++++++++++++++++ 6 files changed, 59 insertions(+), 7 deletions(-) diff --git a/lib/wasix/src/syscalls/journal/actions/close_thread.rs b/lib/wasix/src/syscalls/journal/actions/close_thread.rs index a2fa0b8b0c9..159cafb02b3 100644 --- a/lib/wasix/src/syscalls/journal/actions/close_thread.rs +++ b/lib/wasix/src/syscalls/journal/actions/close_thread.rs @@ -8,8 +8,8 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { exit_code: Option, differ_ethereal: Option<&mut Vec>>, ) -> Result<(), WasiRuntimeError> { - tracing::trace!(%id, ?exit_code, "Replay journal - CloseThread"); if id == self.ctx.data().tid().raw() { + tracing::trace!(%id, ?exit_code, "Replay journal - CloseThread(main)"); if self.bootstrapping { self.clear_ethereal(differ_ethereal); self.staged_differ_memory.clear(); @@ -20,8 +20,10 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { .map_err(anyhow_err_to_runtime_err)?; } } else if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%id, ?exit_code, "Differ(end) journal - CloseThread"); differ_ethereal.push(JournalEntry::CloseThreadV1 { id, exit_code }); } else { + tracing::trace!(%id, ?exit_code, "Replay journal - CloseThread"); JournalEffector::apply_thread_exit( &mut self.ctx, Into::::into(id), diff --git a/lib/wasix/src/syscalls/journal/actions/set_thread.rs b/lib/wasix/src/syscalls/journal/actions/set_thread.rs index 397b5a7f99a..2d21e1de6bf 100644 --- a/lib/wasix/src/syscalls/journal/actions/set_thread.rs +++ b/lib/wasix/src/syscalls/journal/actions/set_thread.rs @@ -13,8 +13,8 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { layout: WasiMemoryLayout, differ_ethereal: Option<&mut Vec>>, ) -> Result<(), WasiRuntimeError> { - tracing::trace!(%id, "Replay journal - SetThread call_stack={} bytes memory_stack={} bytes store_data={} bytes", call_stack.len(), memory_stack.len(), store_data.len()); if Some(self.cur_module_hash) != self.journal_module_hash { + tracing::trace!(%id, "Skipping journal entry - SetThread call_stack={} bytes memory_stack={} bytes store_data={} bytes", call_stack.len(), memory_stack.len(), store_data.len()); return Ok(()); } @@ -28,8 +28,10 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { }; if Into::::into(id) == self.ctx.data().tid() { + tracing::trace!(%id, "Differ(end) journal - SetThread(main) call_stack={} bytes memory_stack={} bytes store_data={} bytes", call_stack.len(), memory_stack.len(), store_data.len()); self.rewind.replace(state); } else if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%id, "Differ(ether) journal - SetThread call_stack={} bytes memory_stack={} bytes store_data={} bytes", call_stack.len(), memory_stack.len(), store_data.len()); differ_ethereal.push(JournalEntry::SetThreadV1 { id, call_stack, @@ -39,6 +41,9 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { layout, is_64bit, }); + } else if self.bootstrapping { + tracing::trace!(%id, "Differ(end) journal - SetThread({id}) call_stack={} bytes memory_stack={} bytes store_data={} bytes", call_stack.len(), memory_stack.len(), store_data.len()); + self.spawn_threads.insert(id.into(), state); } else { return Err(WasiRuntimeError::Runtime(RuntimeError::user( anyhow::format_err!( diff --git a/lib/wasix/src/syscalls/journal/actions/snapshot.rs b/lib/wasix/src/syscalls/journal/actions/snapshot.rs index 18e004d47db..3b6e5439afc 100644 --- a/lib/wasix/src/syscalls/journal/actions/snapshot.rs +++ b/lib/wasix/src/syscalls/journal/actions/snapshot.rs @@ -8,8 +8,6 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { trigger: SnapshotTrigger, differ_ethereal: Option<&mut Vec>>, ) -> Result<(), WasiRuntimeError> { - tracing::trace!("Replay journal - Snapshot"); - // If we are not in the same module then we fire off an exit // that simulates closing the process (hence keeps everything // in a clean state) @@ -26,13 +24,22 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { return Ok(()); } + tracing::trace!("Replay journal - Snapshot (trigger={:?})", trigger); + // Execute all the ethereal events if let Some(ethereal_events) = differ_ethereal { for next in ethereal_events.drain(..) { - tracing::trace!("Ethereal snapshot event - {next:?}"); - self.play_event(next, None)?; + tracing::trace!("Replay(ether) snapshot event - {next:?}"); + if let Err(err) = self.play_event(next, None) { + tracing::warn!("failed to replay event - {}", err); + return Err(err); + } } for (region, data) in self.staged_differ_memory.drain(..) { + tracing::trace!( + "Differ(end) memory event - {region:?} data.len={}", + data.len() + ); self.differ_memory.push((region, data)); } } diff --git a/lib/wasix/src/syscalls/journal/actions/update_memory.rs b/lib/wasix/src/syscalls/journal/actions/update_memory.rs index f04b36cc47c..746e5355e7a 100644 --- a/lib/wasix/src/syscalls/journal/actions/update_memory.rs +++ b/lib/wasix/src/syscalls/journal/actions/update_memory.rs @@ -8,16 +8,19 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { data: Cow<'a, [u8]>, differ_ethereal: Option<&mut Vec>>, ) -> Result<(), WasiRuntimeError> { - tracing::trace!("Replay journal - UpdateMemory"); if Some(self.cur_module_hash) != self.journal_module_hash { + tracing::trace!("Ignored journal - UpdateMemory"); return Ok(()); } if self.bootstrapping { + tracing::trace!("Differ(stage) journal - UpdateMemory"); self.staged_differ_memory.push((region, data)); } else if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!("Differ(ether) journal - UpdateMemory"); differ_ethereal.push(JournalEntry::UpdateMemoryRegionV1 { region, data }); } else { + tracing::trace!("Replay journal - UpdateMemory"); JournalEffector::apply_memory(&mut self.ctx, region, &data) .map_err(anyhow_err_to_runtime_err)?; } diff --git a/lib/wasix/src/syscalls/journal/clear_ethereal.rs b/lib/wasix/src/syscalls/journal/clear_ethereal.rs index 95deb1e039b..2ce4fc9b769 100644 --- a/lib/wasix/src/syscalls/journal/clear_ethereal.rs +++ b/lib/wasix/src/syscalls/journal/clear_ethereal.rs @@ -5,6 +5,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { &mut self, mut differ_ethereal: Option<&mut Vec>>, ) { + tracing::trace!("Replay journal - ClearEthereal"); self.spawn_threads.clear(); self.stdout.clear(); self.stderr.clear(); diff --git a/lib/wasix/src/syscalls/journal/play_event.rs b/lib/wasix/src/syscalls/journal/play_event.rs index 674d078633f..be62674723f 100644 --- a/lib/wasix/src/syscalls/journal/play_event.rs +++ b/lib/wasix/src/syscalls/journal/play_event.rs @@ -28,6 +28,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { if self.real_fd.contains(&fd) { self.action_fd_write(fd, offset, data, is_64bit)?; } else if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, %offset, "Differ(ether) journal - FdWrite"); differ_ethereal.push(JournalEntry::FileDescriptorWriteV1 { fd, offset, @@ -42,6 +43,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { if self.real_fd.contains(&fd) { self.action_fd_seek(fd, offset, whence)?; } else if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, %offset, ?whence, "Differ(ether) journal - FdSeek"); differ_ethereal.push(JournalEntry::FileDescriptorSeekV1 { fd, offset, whence }); } else { self.action_fd_seek(fd, offset, whence)?; @@ -77,6 +79,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { if self.real_fd.contains(&fd) { self.action_fd_close(fd)?; } else if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, "Differ(ether) journal - FdClose"); differ_ethereal.push(JournalEntry::CloseFileDescriptorV1 { fd }); } else { self.action_fd_close(fd)?; @@ -142,6 +145,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { if self.real_fd.remove(&old_fd) { self.action_fd_renumber(old_fd, new_fd)?; } else if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%old_fd, %new_fd, "Differ(ether) journal - FdRenumber"); differ_ethereal.push(JournalEntry::RenumberFileDescriptorV1 { old_fd, new_fd }); } else { self.action_fd_renumber(old_fd, new_fd)?; @@ -154,6 +158,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { if self.real_fd.contains(&original_fd) { self.action_fd_dup(original_fd, copied_fd)?; } else if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%original_fd, %copied_fd, "Differ(ether) journal - FdDuplicate"); differ_ethereal.push(JournalEntry::DuplicateFileDescriptorV1 { original_fd, copied_fd, @@ -178,6 +183,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { if self.real_fd.contains(&fd) { self.action_path_set_times(fd, flags, path, st_atim, st_mtim, fst_flags)?; } else if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, "Differ(ether) journal - PathSetTimes"); differ_ethereal.push(JournalEntry::PathSetTimesV1 { fd, flags, @@ -199,6 +205,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { if self.real_fd.contains(&fd) { self.action_fd_set_times(fd, st_atim, st_mtim, fst_flags)? } else if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, %st_atim, %st_mtim, ?fst_flags, "Differ(ether) journal - FdSetTimes"); differ_ethereal.push(JournalEntry::FileDescriptorSetTimesV1 { fd, st_atim, @@ -213,6 +220,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { if self.real_fd.contains(&fd) { self.action_fd_set_size(fd, st_size)?; } else if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, %st_size, "Differ(ether) journal - FdSetSize"); differ_ethereal.push(JournalEntry::FileDescriptorSetSizeV1 { fd, st_size }); } else { self.action_fd_set_size(fd, st_size)?; @@ -222,6 +230,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { if self.real_fd.contains(&fd) { self.action_fd_set_flags(fd, flags)?; } else if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, ?flags, "Differ(ether) journal - FdSetFlags"); differ_ethereal.push(JournalEntry::FileDescriptorSetFlagsV1 { fd, flags }); } else { self.action_fd_set_flags(fd, flags)?; @@ -235,6 +244,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { if self.real_fd.contains(&fd) { self.action_fd_set_rights(fd, fs_rights_base, fs_rights_inheriting)?; } else if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, "Differ(ether) journal - FdSetRights"); differ_ethereal.push(JournalEntry::FileDescriptorSetRightsV1 { fd, fs_rights_base, @@ -253,6 +263,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { if self.real_fd.contains(&fd) { self.action_fd_advise(fd, offset, len, advice)?; } else if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, %offset, %len, ?advice, "Differ(ether) journal - FdAdvise"); differ_ethereal.push(JournalEntry::FileDescriptorAdviseV1 { fd, offset, @@ -267,6 +278,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { if self.real_fd.contains(&fd) { self.action_fd_allocate(fd, offset, len)?; } else if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, %offset, %len, "Differ(ether) journal - FdAllocate"); differ_ethereal.push(JournalEntry::FileDescriptorAllocateV1 { fd, offset, @@ -304,11 +316,13 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { .map_err(anyhow_err_to_runtime_err)?; } JournalEntry::ChangeDirectoryV1 { path } => { + tracing::trace!("Replay journal - ChangeDirection {}", path); JournalEffector::apply_chdir(&mut self.ctx, &path) .map_err(anyhow_err_to_runtime_err)?; } JournalEntry::CreatePipeV1 { fd1, fd2 } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd1, %fd2, "Differ(ether) journal - CreatePipe"); differ_ethereal.push(JournalEntry::CreatePipeV1 { fd1, fd2 }); } else { tracing::trace!(%fd1, %fd2, "Replay journal - CreatePipe"); @@ -318,6 +332,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { } JournalEntry::EpollCreateV1 { fd } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, "Differ(ether) journal - EpollCreate"); differ_ethereal.push(JournalEntry::EpollCreateV1 { fd }); } else { tracing::trace!(%fd, "Replay journal - EpollCreate"); @@ -332,6 +347,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { event, } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%epfd, %fd, ?op, "Differ(ether) journal - EpollCtl"); differ_ethereal.push(JournalEntry::EpollCtlV1 { epfd, op, @@ -346,6 +362,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { } JournalEntry::TtySetV1 { tty, line_feeds } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!("Differ(ether) journal - TtySet"); differ_ethereal.push(JournalEntry::TtySetV1 { tty, line_feeds }); } else { self.action_tty_set(tty, line_feeds)?; @@ -418,6 +435,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { } JournalEntry::SocketOpenV1 { af, ty, pt, fd } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(?af, ?ty, ?pt, %fd, "Differ(ether) journal - SocketOpen"); differ_ethereal.push(JournalEntry::SocketOpenV1 { af, ty, pt, fd }); } else { tracing::trace!(?af, ?ty, ?pt, %fd, "Replay journal - SocketOpen"); @@ -427,6 +445,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { } JournalEntry::SocketListenV1 { fd, backlog } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, "Differ(ether) journal - SocketListen"); differ_ethereal.push(JournalEntry::SocketListenV1 { fd, backlog }); } else { tracing::trace!(%fd, "Replay journal - SocketListen"); @@ -436,6 +455,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { } JournalEntry::SocketBindV1 { fd, addr } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, ?addr, "Differ(ether) journal - SocketBind"); differ_ethereal.push(JournalEntry::SocketBindV1 { fd, addr }); } else { tracing::trace!(%fd, ?addr, "Replay journal - SocketBind"); @@ -449,6 +469,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { peer_addr, } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, ?peer_addr, "Differ(ether) journal - SockConnect"); differ_ethereal.push(JournalEntry::SocketConnectedV1 { fd, local_addr, @@ -469,6 +490,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { non_blocking: nonblocking, } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%listen_fd, %fd, ?peer_addr, "Differ(ether) journal - SocketAccept"); differ_ethereal.push(JournalEntry::SocketAcceptedV1 { listen_fd, fd, @@ -497,6 +519,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { iface, } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, ?multiaddr, "Differ(ether) journal - JoinIpv4Multicast"); differ_ethereal.push(JournalEntry::SocketJoinIpv4MulticastV1 { fd, multiaddr, @@ -519,6 +542,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { iface, } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, ?multiaddr, "Differ(ether) journal - JoinIpv6Multicast"); differ_ethereal.push(JournalEntry::SocketJoinIpv6MulticastV1 { fd, multi_addr: multiaddr, @@ -541,6 +565,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { iface, } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, ?multiaddr, "Differ(ether) journal - LeaveIpv4Multicast"); differ_ethereal.push(JournalEntry::SocketLeaveIpv4MulticastV1 { fd, multi_addr: multiaddr, @@ -563,6 +588,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { iface, } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, ?multiaddr, "Differ(ether) journal - LeaveIpv6Multicast"); differ_ethereal.push(JournalEntry::SocketLeaveIpv6MulticastV1 { fd, multi_addr: multiaddr, @@ -586,6 +612,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { count, } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%socket_fd, %file_fd, %offset, %count, "Differ(ether) journal - SockSendFile"); differ_ethereal.push(JournalEntry::SocketSendFileV1 { socket_fd, file_fd, @@ -612,6 +639,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { is_64bit, } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, "Differ(ether) journal - SocketSendTo data={} bytes", data.len()); differ_ethereal.push(JournalEntry::SocketSendToV1 { fd, data, @@ -640,6 +668,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { is_64bit, } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, "Differ(ether) journal - SocketSend data={} bytes", data.len()); differ_ethereal.push(JournalEntry::SocketSendV1 { fd, data, @@ -658,6 +687,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { } JournalEntry::SocketSetOptFlagV1 { fd, opt, flag } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, ?opt, %flag, "Differ(ether) journal - SocketSetOptFlag"); differ_ethereal.push(JournalEntry::SocketSetOptFlagV1 { fd, opt, flag }); } else { tracing::trace!(%fd, ?opt, %flag, "Replay journal - SocketSetOptFlag"); @@ -667,6 +697,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { } JournalEntry::SocketSetOptSizeV1 { fd, opt, size } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, ?opt, %size, "Differ(ether) journal - SocketSetOptSize"); differ_ethereal.push(JournalEntry::SocketSetOptSizeV1 { fd, opt, size }); } else { tracing::trace!(%fd, ?opt, %size, "Replay journal - SocketSetOptSize"); @@ -676,6 +707,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { } JournalEntry::SocketSetOptTimeV1 { fd, ty, time } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, ?ty, ?time, "Differ(ether) journal - SocketSetOptTime"); differ_ethereal.push(JournalEntry::SocketSetOptTimeV1 { fd, ty, time }); } else { tracing::trace!(%fd, ?ty, ?time, "Replay journal - SocketSetOptTime"); @@ -685,6 +717,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { } JournalEntry::SocketShutdownV1 { fd, how } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, ?how, "Differ(ether) journal - SocketShutdown"); differ_ethereal.push(JournalEntry::SocketShutdownV1 { fd, how }); } else { tracing::trace!(%fd, ?how, "Replay journal - SocketShutdown"); @@ -698,6 +731,7 @@ impl<'a, 'c> JournalSyscallPlayer<'a, 'c> { fd, } => { if let Some(differ_ethereal) = differ_ethereal { + tracing::trace!(%fd, %flags, "Differ(ether) journal - CreateEvent"); differ_ethereal.push(JournalEntry::CreateEventV1 { initial_val, flags, From f0290c89f14fe802cb6c016609a22b191a089e9d Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Mon, 26 Feb 2024 20:04:32 +1100 Subject: [PATCH 66/67] Fixed yet another linting issue --- lib/wasix/src/runners/dproxy/handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wasix/src/runners/dproxy/handler.rs b/lib/wasix/src/runners/dproxy/handler.rs index 3d1eb035404..ea0cf3d6fbb 100644 --- a/lib/wasix/src/runners/dproxy/handler.rs +++ b/lib/wasix/src/runners/dproxy/handler.rs @@ -58,7 +58,7 @@ impl Handler { .headers() .get("X-Shard") .map(|v| String::from_utf8_lossy(v.as_bytes())) - .map(|s| match (&s).parse::() { + .map(|s| match s.parse::() { Ok(id) => Ok(Shard::ById(id)), Err(err) => Err(err), }) From 028a5a8de3636bbdb4e883af7868416304d41e14 Mon Sep 17 00:00:00 2001 From: Johnathan Sharratt Date: Tue, 27 Feb 2024 17:27:55 +1100 Subject: [PATCH 67/67] Exposing WASIX method so that it can be consumed by custom task managers --- lib/wasix/src/state/env.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wasix/src/state/env.rs b/lib/wasix/src/state/env.rs index beb81da734c..bed4090dee9 100644 --- a/lib/wasix/src/state/env.rs +++ b/lib/wasix/src/state/env.rs @@ -673,7 +673,7 @@ impl WasiEnv { } /// Porcesses any signals that are batched up or any forced exit codes - pub(crate) fn process_signals_and_exit(ctx: &mut FunctionEnvMut<'_, Self>) -> WasiResult { + pub fn process_signals_and_exit(ctx: &mut FunctionEnvMut<'_, Self>) -> WasiResult { // If a signal handler has never been set then we need to handle signals // differently let env = ctx.data();