net_util: Add windows slirp support

BUG=b:237011316
TEST=presubmit and tested in wine

Change-Id: I1b6160142b8161d4b09d3fd98dfacde354e238b4
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/3934818
Reviewed-by: Dennis Kempin <denniskempin@google.com>
Commit-Queue: Vikram Auradkar <auradkar@google.com>
This commit is contained in:
Vikram Auradkar 2022-10-04 20:29:10 +00:00 committed by crosvm LUCI
parent 75dbd9763e
commit c4a4dc9b23
14 changed files with 145 additions and 73 deletions

2
Cargo.lock generated
View file

@ -1260,6 +1260,7 @@ dependencies = [
name = "net_util" name = "net_util"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow",
"base", "base",
"cfg-if", "cfg-if",
"cros_async", "cros_async",
@ -1269,6 +1270,7 @@ dependencies = [
"metrics", "metrics",
"net_sys", "net_sys",
"pcap-file", "pcap-file",
"prebuilts",
"remain", "remain",
"serde", "serde",
"smallvec", "smallvec",

View file

@ -130,7 +130,7 @@ all-linux = [
"wl-dmabuf", "wl-dmabuf",
"x", "x",
] ]
win64 = [ "balloon", "crash_report", "haxm", "stats" ] win64 = [ "balloon", "crash_report", "haxm", "slirp", "stats" ]
arc_quota = ["devices/arc_quota"] arc_quota = ["devices/arc_quota"]
audio = ["devices/audio"] audio = ["devices/audio"]
audio_cras = ["devices/audio_cras"] audio_cras = ["devices/audio_cras"]
@ -169,7 +169,7 @@ plugin = ["protos/plugin", "crosvm_plugin", "kvm", "kvm_sys", "protobuf"]
plugin-render-server = [] plugin-render-server = []
power-monitor-powerd = ["arch/power-monitor-powerd"] power-monitor-powerd = ["arch/power-monitor-powerd"]
qcow = ["disk/qcow"] qcow = ["disk/qcow"]
slirp = ["devices/slirp"] slirp = ["devices/slirp", "net_util/slirp"]
stats = ["devices/stats"] stats = ["devices/stats"]
tpm = ["devices/tpm"] tpm = ["devices/tpm"]
usb = ["devices/usb"] usb = ["devices/usb"]

View file

@ -15,7 +15,6 @@ mod interrupt;
mod iommu; mod iommu;
mod queue; mod queue;
mod rng; mod rng;
#[cfg(unix)]
mod sys; mod sys;
#[cfg(any(feature = "tpm", feature = "vtpm"))] #[cfg(any(feature = "tpm", feature = "vtpm"))]
mod tpm; mod tpm;
@ -80,6 +79,8 @@ cfg_if::cfg_if! {
#[cfg(feature = "slirp")] #[cfg(feature = "slirp")]
pub use self::net::*; pub use self::net::*;
#[cfg(feature = "slirp")]
pub use self::sys::windows::NetExt;
pub use self::vsock::*; pub use self::vsock::*;
} else { } else {
compile_error!("Unsupported platform"); compile_error!("Unsupported platform");

View file

@ -163,13 +163,11 @@ pub(in crate::virtio::vhost::user::device::net) fn start_queue<T: 'static + Into
let tap = ex let tap = ex
.async_from(tap) .async_from(tap)
.context("failed to create async tap device")?; .context("failed to create async tap device")?;
let read_notifier = base::Event( let read_notifier = overlapped_wrapper
overlapped_wrapper .get_h_event_ref()
.get_h_event_ref() .unwrap()
.unwrap() .try_clone()
.try_clone() .unwrap();
.unwrap(),
);
let read_notifier = EventAsync::new_without_reset(read_notifier, ex) let read_notifier = EventAsync::new_without_reset(read_notifier, ex)
.context("failed to create async read notifier")?; .context("failed to create async read notifier")?;

View file

@ -17,7 +17,6 @@ cfg-if = "1.0.0"
cros_async = { path = "../cros_async" } cros_async = { path = "../cros_async" }
data_model = { path = "../common/data_model" } data_model = { path = "../common/data_model" }
libc = "*" libc = "*"
libslirp-sys = { version = "4.2.1", optional = true }
net_sys = { path = "../net_sys" } net_sys = { path = "../net_sys" }
pcap-file = { version = "1.1.0", optional = true } pcap-file = { version = "1.1.0", optional = true }
@ -30,3 +29,8 @@ virtio_sys = { path = "../virtio_sys" }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
metrics = { path = "../metrics" } metrics = { path = "../metrics" }
winapi = { version = "*", features = ["everything", "std", "impl-default"] } winapi = { version = "*", features = ["everything", "std", "impl-default"] }
libslirp-sys = { version = "4.2.1", optional = true }
[build-dependencies]
anyhow = "*"
prebuilts = { path = "../prebuilts" }

View file

@ -2,33 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#[cfg(all(feature = "slirp", windows))] static PREBUILTS_VERSION_FILENAME: &str = "prebuilts_version";
mod win_slirp { static SLIRP_LIB: &str = "libslirp.lib";
use std::env; static SLIRP_DLL: &str = "libslirp-0.dll";
#[cfg(unix)]
pub(super) fn main() { static GLIB_FILENAME: &str = "libglib-2.0.dll.a";
// This must be an absolute path or linking issues will result when a consuming crate
// tries to link since $PWD will be different.
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
#[cfg(debug_assertions)]
let build_type = "debug";
#[cfg(not(debug_assertions))]
let build_type = "release";
println!(
r#"cargo:rustc-link-search={}\..\..\..\third_party\libslirp\{}"#,
manifest_dir, build_type
);
println!(
r#"cargo:rustc-env=PATH={};{}\..\..\..\third_party\libslirp\{};"#,
env::var("PATH").unwrap(),
manifest_dir,
build_type,
);
}
}
fn main() { fn main() {
// We (the Windows crosvm maintainers) submitted upstream patches to libslirp-sys so it doesn't // We (the Windows crosvm maintainers) submitted upstream patches to libslirp-sys so it doesn't
@ -36,8 +14,27 @@ fn main() {
// to the build system that invokes Cargo (e.g. the crosvm jCI scripts that also produce the // to the build system that invokes Cargo (e.g. the crosvm jCI scripts that also produce the
// required libslirp DLL & lib). The integration here (win_slirp::main) is specific to crosvm's // required libslirp DLL & lib). The integration here (win_slirp::main) is specific to crosvm's
// build process. // build process.
#[cfg(all(feature = "slirp", windows))] if std::env::var("CARGO_CFG_WINDOWS").is_ok() {
win_slirp::main(); let version = std::fs::read_to_string(PREBUILTS_VERSION_FILENAME)
.unwrap()
.trim()
.parse::<u32>()
.unwrap();
// TODO(b:242204245) build libslirp locally on windows from build.rs.
prebuilts::download_prebuilts(
"libslirp",
version,
&[
SLIRP_DLL,
SLIRP_LIB,
#[cfg(unix)]
// When compiling with mingw64 to run under wine64, we need glib as slirp links
// against it.
GLIB_FILENAME,
],
)
.unwrap();
}
// For unix, libslirp-sys's build script will make the appropriate linking calls to pkg_config. // For unix, libslirp-sys's build script will make the appropriate linking calls to pkg_config.
} }

View file

@ -0,0 +1 @@
1

View file

@ -33,6 +33,11 @@ use serde::Serialize;
pub use sys::TapT; pub use sys::TapT;
use thiserror::Error as ThisError; use thiserror::Error as ThisError;
#[cfg(all(feature = "slirp"))]
pub mod slirp;
#[cfg(all(feature = "slirp", windows))]
pub use slirp::Slirp;
#[sorted] #[sorted]
#[derive(ThisError, Debug)] #[derive(ThisError, Debug)]
pub enum Error { pub enum Error {
@ -51,18 +56,23 @@ pub enum Error {
/// Couldn't open /dev/net/tun. /// Couldn't open /dev/net/tun.
#[error("failed to open /dev/net/tun: {0}")] #[error("failed to open /dev/net/tun: {0}")]
OpenTun(SysError), OpenTun(SysError),
#[cfg(all(feature = "slirp", windows))]
#[error("slirp related error")]
Slirp(slirp::SlirpError),
} }
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
impl Error { impl Error {
pub fn sys_error(&self) -> SysError { pub fn sys_error(&self) -> SysError {
match *self { match &*self {
Error::CreateSocket(e) => e, Error::CreateSocket(e) => *e,
Error::OpenTun(e) => e, Error::OpenTun(e) => *e,
Error::CreateTap(e) => e, Error::CreateTap(e) => *e,
Error::CloneTap(e) => e, Error::CloneTap(e) => *e,
Error::IoctlError(e) => e, Error::IoctlError(e) => *e,
#[cfg(all(feature = "slirp", windows))]
Error::Slirp(e) => e.sys_error(),
} }
} }
} }

View file

@ -6,6 +6,8 @@
//! level interfaces to libslirp that are used to implement that loop, and //! level interfaces to libslirp that are used to implement that loop, and
//! diagnostic tools. //! diagnostic tools.
#![cfg(windows)]
#[path = "../../third_party/libslirp-rs/src/context.rs"] #[path = "../../third_party/libslirp-rs/src/context.rs"]
pub mod context; pub mod context;
@ -15,6 +17,44 @@ pub mod packet_ring_buffer;
pub mod sys; pub mod sys;
pub use sys::Slirp; pub use sys::Slirp;
use base::Error as SysError;
use remain::sorted;
use thiserror::Error as ThisError;
/// Length includes space for an ethernet frame & the vnet header. See the virtio spec for details: /// Length includes space for an ethernet frame & the vnet header. See the virtio spec for details:
/// <http://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-2050006> /// <http://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-2050006>
pub const ETHERNET_FRAME_SIZE: usize = 1526; pub const ETHERNET_FRAME_SIZE: usize = 1526;
#[cfg(windows)]
#[sorted]
#[derive(ThisError, Debug)]
pub enum SlirpError {
#[error("pipe was closed: {0}")]
BrokenPipe(std::io::Error),
#[error("failed to clone object: {0}")]
CloneFailed(std::io::Error),
#[error("overlapped operation failed: {0}")]
OverlappedError(std::io::Error),
/// Error encountered while in a Slirp related poll operation.
#[error("slirp poll failed: {0}")]
SlirpIOPollError(std::io::Error),
/// Error encountered while in a Slirp related poll operation.
#[error("slirp poll failed: {0}")]
SlirpPollError(SysError),
#[error("WSAStartup failed with code: {0}")]
WSAStartupError(SysError),
}
#[cfg(windows)]
impl SlirpError {
pub fn sys_error(&self) -> SysError {
match &*self {
SlirpError::BrokenPipe(e) => SysError::new(e.raw_os_error().unwrap_or_default()),
SlirpError::CloneFailed(e) => SysError::new(e.raw_os_error().unwrap_or_default()),
SlirpError::OverlappedError(e) => SysError::new(e.raw_os_error().unwrap_or_default()),
SlirpError::SlirpIOPollError(e) => SysError::new(e.raw_os_error().unwrap_or_default()),
SlirpError::SlirpPollError(e) => *e,
SlirpError::WSAStartupError(e) => *e,
}
}
}

View file

@ -30,6 +30,7 @@ use cros_async::IntoAsync;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use crate::slirp::SlirpError;
use crate::slirp::ETHERNET_FRAME_SIZE; use crate::slirp::ETHERNET_FRAME_SIZE;
use crate::Error; use crate::Error;
use crate::MacAddress; use crate::MacAddress;
@ -69,7 +70,7 @@ impl Slirp {
#[cfg(feature = "slirp-ring-capture")] #[cfg(feature = "slirp-ring-capture")]
let slirp_capture_file_clone = slirp_capture_file.clone(); let slirp_capture_file_clone = slirp_capture_file.clone();
slirp_thread = thread::spawn(move || { slirp_thread = thread::spawn(move || {
let disable_access_to_host = !cfg!("guest-to-host-net-loopback"); let disable_access_to_host = !cfg!(feature = "guest-to-host-net-loopback");
handler::start_slirp( handler::start_slirp(
slirp_pipe, slirp_pipe,
@ -100,7 +101,10 @@ impl Slirp {
fn try_clone(&self) -> Result<Self> { fn try_clone(&self) -> Result<Self> {
Ok(Slirp { Ok(Slirp {
guest_pipe: self.guest_pipe.try_clone().map_err(Error::CloneFailed)?, guest_pipe: self
.guest_pipe
.try_clone()
.map_err(|e| Error::Slirp(SlirpError::CloneFailed(e)))?,
overlapped_wrapper: OverlappedWrapper::new(true).unwrap(), overlapped_wrapper: OverlappedWrapper::new(true).unwrap(),
slirp_thread: None, slirp_thread: None,
}) })
@ -122,7 +126,7 @@ impl Slirp {
// because libslirp logs *everything* as a debug entry. // because libslirp logs *everything* as a debug entry.
std::env::set_var("G_MESSAGES_DEBUG", "all"); std::env::set_var("G_MESSAGES_DEBUG", "all");
let disable_access_to_host = !cfg!("guest-to-host-net-loopback"); let disable_access_to_host = !cfg!(feature = "guest-to-host-net-loopback");
info!("starting slirp loop..."); info!("starting slirp loop...");
match handler::start_slirp( match handler::start_slirp(
@ -132,7 +136,9 @@ impl Slirp {
#[cfg(feature = "slirp-ring-capture")] #[cfg(feature = "slirp-ring-capture")]
slirp_capture_file.take(), slirp_capture_file.take(),
) { ) {
Err(Error::BrokenPipe(e)) => warn!("exited slirp listening loop: {}", e), Err(Error::Slirp(SlirpError::BrokenPipe(e))) => {
warn!("exited slirp listening loop: {}", e)
}
Err(e) => panic!("error while running slirp listening loop: {}", e), Err(e) => panic!("error while running slirp listening loop: {}", e),
_ => {} _ => {}
} }
@ -226,7 +232,7 @@ impl TapTCommon for Slirp {
unimplemented!("not used by Slirp"); unimplemented!("not used by Slirp");
} }
fn if_flags(&self) -> i32 { fn if_flags(&self) -> u32 {
// This function is unused by the Slirp code paths. // This function is unused by the Slirp code paths.
unimplemented!("not used by Slirp"); unimplemented!("not used by Slirp");
} }

View file

@ -21,20 +21,20 @@ use base::AsRawDescriptor;
use base::Descriptor; use base::Descriptor;
use base::Error as SysError; use base::Error as SysError;
use base::Event; use base::Event;
use base::EventExt;
use base::EventToken; use base::EventToken;
use base::EventWindows;
use base::RawDescriptor; use base::RawDescriptor;
use base::Timer; use base::Timer;
use base::WaitContext; use base::WaitContext;
use base::WaitContextExt;
use data_model::DataInit; use data_model::DataInit;
use data_model::Le16;
use metrics::MetricEventType; use metrics::MetricEventType;
use metrics::PeriodicLogger; use metrics::PeriodicLogger;
#[cfg(any(feature = "slirp-ring-capture", feature = "slirp-debug"))] #[cfg(any(feature = "slirp-ring-capture", feature = "slirp-debug"))]
use pcap_file::pcap::PcapWriter; use pcap_file::pcap::PcapWriter;
use smallvec::SmallVec; use smallvec::SmallVec;
use virtio_sys::virtio_net_hdr; use virtio_sys::virtio_net::virtio_net_hdr;
use virtio_sys::virtio_net_hdr_mrg_rxbuf; use virtio_sys::virtio_net::virtio_net_hdr_mrg_rxbuf;
use winapi::shared::minwindef::MAKEWORD; use winapi::shared::minwindef::MAKEWORD;
use winapi::um::winnt::LONG; use winapi::um::winnt::LONG;
use winapi::um::winnt::SHORT; use winapi::um::winnt::SHORT;
@ -61,6 +61,7 @@ use crate::slirp::context::Context;
use crate::slirp::context::PollEvents; use crate::slirp::context::PollEvents;
#[cfg(feature = "slirp-ring-capture")] #[cfg(feature = "slirp-ring-capture")]
use crate::slirp::packet_ring_buffer::PacketRingBuffer; use crate::slirp::packet_ring_buffer::PacketRingBuffer;
use crate::slirp::SlirpError;
use crate::slirp::ETHERNET_FRAME_SIZE; use crate::slirp::ETHERNET_FRAME_SIZE;
use crate::Error; use crate::Error;
use crate::Result; use crate::Result;
@ -113,7 +114,7 @@ impl CallbackHandler for Handler {
hdr_len: 0, hdr_len: 0,
csum_start: 0, csum_start: 0,
csum_offset: 0, csum_offset: 0,
gso_type: virtio_sys::VIRTIO_NET_HDR_GSO_NONE as u8, gso_type: virtio_sys::virtio_net::VIRTIO_NET_HDR_GSO_NONE as u8,
}, },
num_buffers: 1, num_buffers: 1,
}; };
@ -359,7 +360,7 @@ impl<'a> EventSelectedSocket<'a> {
) )
}; };
if res == SOCKET_ERROR { if res == SOCKET_ERROR {
return Err(Error::SlirpIOPollError(last_wsa_error())); return Err(Error::Slirp(SlirpError::SlirpIOPollError(last_wsa_error())));
} }
Ok(EventSelectedSocket { socket, event }) Ok(EventSelectedSocket { socket, event })
} }
@ -402,28 +403,32 @@ fn poll<'a>(
selected_sockets.push(EventSelectedSocket::new(*socket, socket_event_handle)?); selected_sockets.push(EventSelectedSocket::new(*socket, socket_event_handle)?);
} }
wait_ctx.clear().map_err(Error::SlirpPollError)?; wait_ctx
.clear()
.map_err(|e| Error::Slirp(SlirpError::SlirpPollError(e)))?;
for (i, handle) in handles.iter().enumerate() { for (i, handle) in handles.iter().enumerate() {
match wait_ctx.add(*handle, Token::EventHandleReady(i)) { match wait_ctx.add(*handle, Token::EventHandleReady(i)) {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
return Err(Error::SlirpPollError(e)); return Err(Error::Slirp(SlirpError::SlirpPollError(e)));
} }
} }
} }
match wait_ctx.add(socket_event_handle, Token::SocketReady) { match wait_ctx.add(socket_event_handle, Token::SocketReady) {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
return Err(Error::SlirpPollError(e)); return Err(Error::Slirp(SlirpError::SlirpPollError(e)));
} }
} }
let events = if let Some(timeout) = timeout { let events = if let Some(timeout) = timeout {
wait_ctx wait_ctx
.wait_timeout(timeout) .wait_timeout(timeout)
.map_err(Error::SlirpPollError)? .map_err(|e| Error::Slirp(SlirpError::SlirpPollError(e)))?
} else { } else {
wait_ctx.wait().map_err(Error::SlirpPollError)? wait_ctx
.wait()
.map_err(|e| Error::Slirp(SlirpError::SlirpPollError(e)))?
}; };
let tokens: Vec<Token> = events let tokens: Vec<Token> = events
@ -446,7 +451,7 @@ fn poll<'a>(
let socket_results = if sockets.is_empty() { let socket_results = if sockets.is_empty() {
Vec::new() Vec::new()
} else { } else {
poll_sockets(sockets).map_err(Error::SlirpIOPollError)? poll_sockets(sockets).map_err(|e| Error::Slirp(SlirpError::SlirpIOPollError(e)))?
}; };
Ok((handle_results, socket_results)) Ok((handle_results, socket_results))
@ -466,7 +471,9 @@ impl WSAContext {
// Safe because ctx.data is guaranteed to exist, and we check the return code. // Safe because ctx.data is guaranteed to exist, and we check the return code.
let err = unsafe { WSAStartup(MAKEWORD(2, 0), &mut ctx.data) }; let err = unsafe { WSAStartup(MAKEWORD(2, 0), &mut ctx.data) };
if err != 0 { if err != 0 {
Err(Error::WSAStartupError(SysError::new(err))) Err(Error::Slirp(SlirpError::WSAStartupError(SysError::new(
err,
))))
} else { } else {
Ok(ctx) Ok(ctx)
} }
@ -506,8 +513,10 @@ pub fn start_slirp(
let shutdown_event_handle = shutdown_event.as_raw_descriptor(); let shutdown_event_handle = shutdown_event.as_raw_descriptor();
// Stack data for the poll function. // Stack data for the poll function.
let wait_ctx: WaitContext<Token> = WaitContext::new().map_err(Error::SlirpPollError)?; let wait_ctx: WaitContext<Token> =
let socket_event_handle = Event::new_auto_reset().map_err(Error::SlirpPollError)?; WaitContext::new().map_err(|e| Error::Slirp(SlirpError::SlirpPollError(e)))?;
let socket_event_handle =
Event::new_auto_reset().map_err(|e| Error::Slirp(SlirpError::SlirpPollError(e)))?;
'slirp: loop { 'slirp: loop {
// Request the FDs that we should poll from Slirp. Slirp provides them to us by way of a // Request the FDs that we should poll from Slirp. Slirp provides them to us by way of a

View file

@ -74,6 +74,7 @@ pub(crate) fn run_slirp(args: RunSlirpCommand) -> Result<()> {
let slirp_config = bootstrap_tube.recv::<SlirpStartupConfig>().unwrap(); let slirp_config = bootstrap_tube.recv::<SlirpStartupConfig>().unwrap();
#[cfg(feature = "sandbox")]
if let Some(mut target) = sandbox::TargetServices::get() if let Some(mut target) = sandbox::TargetServices::get()
.exit_context(Exit::SandboxError, "sandbox operation failed")? .exit_context(Exit::SandboxError, "sandbox operation failed")?
{ {

View file

@ -52,6 +52,7 @@ use base::error;
use base::RawDescriptor; use base::RawDescriptor;
use libslirp_sys::*; use libslirp_sys::*;
use crate::slirp::SlirpError;
use crate::Error; use crate::Error;
use crate::Result; use crate::Result;
@ -512,10 +513,10 @@ impl<H: CallbackHandler> Context<H> {
assert!(!slirp.is_null()); assert!(!slirp.is_null());
match ret.callback_handler.begin_read_from_guest() { match ret.callback_handler.begin_read_from_guest() {
Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => { Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => {
return Err(Error::BrokenPipe(e)); return Err(Error::Slirp(SlirpError::BrokenPipe(e)));
} }
Err(e) => { Err(e) => {
return Err(Error::OverlappedError(e)); return Err(Error::Slirp(SlirpError::OverlappedError(e)));
} }
_ => {} _ => {}
} }
@ -545,15 +546,15 @@ impl<H: CallbackHandler> Context<H> {
break; break;
} }
Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => { Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => {
return Err(Error::BrokenPipe(e)); return Err(Error::Slirp(SlirpError::BrokenPipe(e)));
} }
Err(_) => { Err(_) => {
match self.callback_handler.begin_read_from_guest() { match self.callback_handler.begin_read_from_guest() {
Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => { Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => {
return Err(Error::BrokenPipe(e)); return Err(Error::Slirp(SlirpError::BrokenPipe(e)));
} }
Err(e) => { Err(e) => {
return Err(Error::OverlappedError(e)); return Err(Error::Slirp(SlirpError::OverlappedError(e)));
} }
_ => {} _ => {}
} }
@ -562,10 +563,10 @@ impl<H: CallbackHandler> Context<H> {
} }
match self.callback_handler.begin_read_from_guest() { match self.callback_handler.begin_read_from_guest() {
Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => { Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => {
return Err(Error::BrokenPipe(e)); return Err(Error::Slirp(SlirpError::BrokenPipe(e)));
} }
Err(e) => { Err(e) => {
return Err(Error::OverlappedError(e)); return Err(Error::Slirp(SlirpError::OverlappedError(e)));
} }
_ => {} _ => {}
} }

View file

@ -22,7 +22,9 @@ sudo apt-get install --yes --no-install-recommends \
libdbus-1-dev \ libdbus-1-dev \
libdrm-dev \ libdrm-dev \
libepoxy-dev \ libepoxy-dev \
libglib2.0-dev \
libguestfs-tools \ libguestfs-tools \
libslirp-dev \
libssl-dev \ libssl-dev \
libswscale-dev \ libswscale-dev \
libudev-dev \ libudev-dev \