mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-08 19:33:07 +00:00
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:
parent
75dbd9763e
commit
c4a4dc9b23
14 changed files with 145 additions and 73 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1260,6 +1260,7 @@ dependencies = [
|
|||
name = "net_util"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base",
|
||||
"cfg-if",
|
||||
"cros_async",
|
||||
|
@ -1269,6 +1270,7 @@ dependencies = [
|
|||
"metrics",
|
||||
"net_sys",
|
||||
"pcap-file",
|
||||
"prebuilts",
|
||||
"remain",
|
||||
"serde",
|
||||
"smallvec",
|
||||
|
|
|
@ -130,7 +130,7 @@ all-linux = [
|
|||
"wl-dmabuf",
|
||||
"x",
|
||||
]
|
||||
win64 = [ "balloon", "crash_report", "haxm", "stats" ]
|
||||
win64 = [ "balloon", "crash_report", "haxm", "slirp", "stats" ]
|
||||
arc_quota = ["devices/arc_quota"]
|
||||
audio = ["devices/audio"]
|
||||
audio_cras = ["devices/audio_cras"]
|
||||
|
@ -169,7 +169,7 @@ plugin = ["protos/plugin", "crosvm_plugin", "kvm", "kvm_sys", "protobuf"]
|
|||
plugin-render-server = []
|
||||
power-monitor-powerd = ["arch/power-monitor-powerd"]
|
||||
qcow = ["disk/qcow"]
|
||||
slirp = ["devices/slirp"]
|
||||
slirp = ["devices/slirp", "net_util/slirp"]
|
||||
stats = ["devices/stats"]
|
||||
tpm = ["devices/tpm"]
|
||||
usb = ["devices/usb"]
|
||||
|
|
|
@ -15,7 +15,6 @@ mod interrupt;
|
|||
mod iommu;
|
||||
mod queue;
|
||||
mod rng;
|
||||
#[cfg(unix)]
|
||||
mod sys;
|
||||
#[cfg(any(feature = "tpm", feature = "vtpm"))]
|
||||
mod tpm;
|
||||
|
@ -80,6 +79,8 @@ cfg_if::cfg_if! {
|
|||
|
||||
#[cfg(feature = "slirp")]
|
||||
pub use self::net::*;
|
||||
#[cfg(feature = "slirp")]
|
||||
pub use self::sys::windows::NetExt;
|
||||
pub use self::vsock::*;
|
||||
} else {
|
||||
compile_error!("Unsupported platform");
|
||||
|
|
|
@ -163,13 +163,11 @@ pub(in crate::virtio::vhost::user::device::net) fn start_queue<T: 'static + Into
|
|||
let tap = ex
|
||||
.async_from(tap)
|
||||
.context("failed to create async tap device")?;
|
||||
let read_notifier = base::Event(
|
||||
overlapped_wrapper
|
||||
.get_h_event_ref()
|
||||
.unwrap()
|
||||
.try_clone()
|
||||
.unwrap(),
|
||||
);
|
||||
let read_notifier = overlapped_wrapper
|
||||
.get_h_event_ref()
|
||||
.unwrap()
|
||||
.try_clone()
|
||||
.unwrap();
|
||||
let read_notifier = EventAsync::new_without_reset(read_notifier, ex)
|
||||
.context("failed to create async read notifier")?;
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ cfg-if = "1.0.0"
|
|||
cros_async = { path = "../cros_async" }
|
||||
data_model = { path = "../common/data_model" }
|
||||
libc = "*"
|
||||
libslirp-sys = { version = "4.2.1", optional = true }
|
||||
|
||||
net_sys = { path = "../net_sys" }
|
||||
pcap-file = { version = "1.1.0", optional = true }
|
||||
|
@ -30,3 +29,8 @@ virtio_sys = { path = "../virtio_sys" }
|
|||
[target.'cfg(windows)'.dependencies]
|
||||
metrics = { path = "../metrics" }
|
||||
winapi = { version = "*", features = ["everything", "std", "impl-default"] }
|
||||
libslirp-sys = { version = "4.2.1", optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "*"
|
||||
prebuilts = { path = "../prebuilts" }
|
||||
|
|
|
@ -2,33 +2,11 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#[cfg(all(feature = "slirp", windows))]
|
||||
mod win_slirp {
|
||||
use std::env;
|
||||
|
||||
pub(super) fn main() {
|
||||
// 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
static PREBUILTS_VERSION_FILENAME: &str = "prebuilts_version";
|
||||
static SLIRP_LIB: &str = "libslirp.lib";
|
||||
static SLIRP_DLL: &str = "libslirp-0.dll";
|
||||
#[cfg(unix)]
|
||||
static GLIB_FILENAME: &str = "libglib-2.0.dll.a";
|
||||
|
||||
fn main() {
|
||||
// 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
|
||||
// required libslirp DLL & lib). The integration here (win_slirp::main) is specific to crosvm's
|
||||
// build process.
|
||||
#[cfg(all(feature = "slirp", windows))]
|
||||
win_slirp::main();
|
||||
if std::env::var("CARGO_CFG_WINDOWS").is_ok() {
|
||||
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.
|
||||
}
|
||||
|
|
1
net_util/prebuilts_version
Normal file
1
net_util/prebuilts_version
Normal file
|
@ -0,0 +1 @@
|
|||
1
|
|
@ -33,6 +33,11 @@ use serde::Serialize;
|
|||
pub use sys::TapT;
|
||||
use thiserror::Error as ThisError;
|
||||
|
||||
#[cfg(all(feature = "slirp"))]
|
||||
pub mod slirp;
|
||||
#[cfg(all(feature = "slirp", windows))]
|
||||
pub use slirp::Slirp;
|
||||
|
||||
#[sorted]
|
||||
#[derive(ThisError, Debug)]
|
||||
pub enum Error {
|
||||
|
@ -51,18 +56,23 @@ pub enum Error {
|
|||
/// Couldn't open /dev/net/tun.
|
||||
#[error("failed to open /dev/net/tun: {0}")]
|
||||
OpenTun(SysError),
|
||||
#[cfg(all(feature = "slirp", windows))]
|
||||
#[error("slirp related error")]
|
||||
Slirp(slirp::SlirpError),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
impl Error {
|
||||
pub fn sys_error(&self) -> SysError {
|
||||
match *self {
|
||||
Error::CreateSocket(e) => e,
|
||||
Error::OpenTun(e) => e,
|
||||
Error::CreateTap(e) => e,
|
||||
Error::CloneTap(e) => e,
|
||||
Error::IoctlError(e) => e,
|
||||
match &*self {
|
||||
Error::CreateSocket(e) => *e,
|
||||
Error::OpenTun(e) => *e,
|
||||
Error::CreateTap(e) => *e,
|
||||
Error::CloneTap(e) => *e,
|
||||
Error::IoctlError(e) => *e,
|
||||
#[cfg(all(feature = "slirp", windows))]
|
||||
Error::Slirp(e) => e.sys_error(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
//! level interfaces to libslirp that are used to implement that loop, and
|
||||
//! diagnostic tools.
|
||||
|
||||
#![cfg(windows)]
|
||||
|
||||
#[path = "../../third_party/libslirp-rs/src/context.rs"]
|
||||
pub mod context;
|
||||
|
||||
|
@ -15,6 +17,44 @@ pub mod packet_ring_buffer;
|
|||
pub mod sys;
|
||||
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:
|
||||
/// <http://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-2050006>
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ use cros_async::IntoAsync;
|
|||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::slirp::SlirpError;
|
||||
use crate::slirp::ETHERNET_FRAME_SIZE;
|
||||
use crate::Error;
|
||||
use crate::MacAddress;
|
||||
|
@ -69,7 +70,7 @@ impl Slirp {
|
|||
#[cfg(feature = "slirp-ring-capture")]
|
||||
let slirp_capture_file_clone = slirp_capture_file.clone();
|
||||
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(
|
||||
slirp_pipe,
|
||||
|
@ -100,7 +101,10 @@ impl Slirp {
|
|||
|
||||
fn try_clone(&self) -> Result<Self> {
|
||||
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(),
|
||||
slirp_thread: None,
|
||||
})
|
||||
|
@ -122,7 +126,7 @@ impl Slirp {
|
|||
// because libslirp logs *everything* as a debug entry.
|
||||
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...");
|
||||
match handler::start_slirp(
|
||||
|
@ -132,7 +136,9 @@ impl Slirp {
|
|||
#[cfg(feature = "slirp-ring-capture")]
|
||||
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),
|
||||
_ => {}
|
||||
}
|
||||
|
@ -226,7 +232,7 @@ impl TapTCommon for 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.
|
||||
unimplemented!("not used by Slirp");
|
||||
}
|
||||
|
|
|
@ -21,20 +21,20 @@ use base::AsRawDescriptor;
|
|||
use base::Descriptor;
|
||||
use base::Error as SysError;
|
||||
use base::Event;
|
||||
use base::EventExt;
|
||||
use base::EventToken;
|
||||
use base::EventWindows;
|
||||
use base::RawDescriptor;
|
||||
use base::Timer;
|
||||
use base::WaitContext;
|
||||
use base::WaitContextExt;
|
||||
use data_model::DataInit;
|
||||
use data_model::Le16;
|
||||
use metrics::MetricEventType;
|
||||
use metrics::PeriodicLogger;
|
||||
#[cfg(any(feature = "slirp-ring-capture", feature = "slirp-debug"))]
|
||||
use pcap_file::pcap::PcapWriter;
|
||||
use smallvec::SmallVec;
|
||||
use virtio_sys::virtio_net_hdr;
|
||||
use virtio_sys::virtio_net_hdr_mrg_rxbuf;
|
||||
use virtio_sys::virtio_net::virtio_net_hdr;
|
||||
use virtio_sys::virtio_net::virtio_net_hdr_mrg_rxbuf;
|
||||
use winapi::shared::minwindef::MAKEWORD;
|
||||
use winapi::um::winnt::LONG;
|
||||
use winapi::um::winnt::SHORT;
|
||||
|
@ -61,6 +61,7 @@ use crate::slirp::context::Context;
|
|||
use crate::slirp::context::PollEvents;
|
||||
#[cfg(feature = "slirp-ring-capture")]
|
||||
use crate::slirp::packet_ring_buffer::PacketRingBuffer;
|
||||
use crate::slirp::SlirpError;
|
||||
use crate::slirp::ETHERNET_FRAME_SIZE;
|
||||
use crate::Error;
|
||||
use crate::Result;
|
||||
|
@ -113,7 +114,7 @@ impl CallbackHandler for Handler {
|
|||
hdr_len: 0,
|
||||
csum_start: 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,
|
||||
};
|
||||
|
@ -359,7 +360,7 @@ impl<'a> EventSelectedSocket<'a> {
|
|||
)
|
||||
};
|
||||
if res == SOCKET_ERROR {
|
||||
return Err(Error::SlirpIOPollError(last_wsa_error()));
|
||||
return Err(Error::Slirp(SlirpError::SlirpIOPollError(last_wsa_error())));
|
||||
}
|
||||
Ok(EventSelectedSocket { socket, event })
|
||||
}
|
||||
|
@ -402,28 +403,32 @@ fn poll<'a>(
|
|||
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() {
|
||||
match wait_ctx.add(*handle, Token::EventHandleReady(i)) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
return Err(Error::SlirpPollError(e));
|
||||
return Err(Error::Slirp(SlirpError::SlirpPollError(e)));
|
||||
}
|
||||
}
|
||||
}
|
||||
match wait_ctx.add(socket_event_handle, Token::SocketReady) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
return Err(Error::SlirpPollError(e));
|
||||
return Err(Error::Slirp(SlirpError::SlirpPollError(e)));
|
||||
}
|
||||
}
|
||||
|
||||
let events = if let Some(timeout) = timeout {
|
||||
wait_ctx
|
||||
.wait_timeout(timeout)
|
||||
.map_err(Error::SlirpPollError)?
|
||||
.map_err(|e| Error::Slirp(SlirpError::SlirpPollError(e)))?
|
||||
} else {
|
||||
wait_ctx.wait().map_err(Error::SlirpPollError)?
|
||||
wait_ctx
|
||||
.wait()
|
||||
.map_err(|e| Error::Slirp(SlirpError::SlirpPollError(e)))?
|
||||
};
|
||||
|
||||
let tokens: Vec<Token> = events
|
||||
|
@ -446,7 +451,7 @@ fn poll<'a>(
|
|||
let socket_results = if sockets.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
poll_sockets(sockets).map_err(Error::SlirpIOPollError)?
|
||||
poll_sockets(sockets).map_err(|e| Error::Slirp(SlirpError::SlirpIOPollError(e)))?
|
||||
};
|
||||
|
||||
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.
|
||||
let err = unsafe { WSAStartup(MAKEWORD(2, 0), &mut ctx.data) };
|
||||
if err != 0 {
|
||||
Err(Error::WSAStartupError(SysError::new(err)))
|
||||
Err(Error::Slirp(SlirpError::WSAStartupError(SysError::new(
|
||||
err,
|
||||
))))
|
||||
} else {
|
||||
Ok(ctx)
|
||||
}
|
||||
|
@ -506,8 +513,10 @@ pub fn start_slirp(
|
|||
let shutdown_event_handle = shutdown_event.as_raw_descriptor();
|
||||
|
||||
// Stack data for the poll function.
|
||||
let wait_ctx: WaitContext<Token> = WaitContext::new().map_err(Error::SlirpPollError)?;
|
||||
let socket_event_handle = Event::new_auto_reset().map_err(Error::SlirpPollError)?;
|
||||
let wait_ctx: WaitContext<Token> =
|
||||
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 {
|
||||
// Request the FDs that we should poll from Slirp. Slirp provides them to us by way of a
|
||||
|
|
|
@ -74,6 +74,7 @@ pub(crate) fn run_slirp(args: RunSlirpCommand) -> Result<()> {
|
|||
|
||||
let slirp_config = bootstrap_tube.recv::<SlirpStartupConfig>().unwrap();
|
||||
|
||||
#[cfg(feature = "sandbox")]
|
||||
if let Some(mut target) = sandbox::TargetServices::get()
|
||||
.exit_context(Exit::SandboxError, "sandbox operation failed")?
|
||||
{
|
||||
|
|
15
third_party/libslirp-rs/src/context.rs
vendored
15
third_party/libslirp-rs/src/context.rs
vendored
|
@ -52,6 +52,7 @@ use base::error;
|
|||
use base::RawDescriptor;
|
||||
use libslirp_sys::*;
|
||||
|
||||
use crate::slirp::SlirpError;
|
||||
use crate::Error;
|
||||
use crate::Result;
|
||||
|
||||
|
@ -512,10 +513,10 @@ impl<H: CallbackHandler> Context<H> {
|
|||
assert!(!slirp.is_null());
|
||||
match ret.callback_handler.begin_read_from_guest() {
|
||||
Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => {
|
||||
return Err(Error::BrokenPipe(e));
|
||||
return Err(Error::Slirp(SlirpError::BrokenPipe(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;
|
||||
}
|
||||
Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => {
|
||||
return Err(Error::BrokenPipe(e));
|
||||
return Err(Error::Slirp(SlirpError::BrokenPipe(e)));
|
||||
}
|
||||
Err(_) => {
|
||||
match self.callback_handler.begin_read_from_guest() {
|
||||
Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => {
|
||||
return Err(Error::BrokenPipe(e));
|
||||
return Err(Error::Slirp(SlirpError::BrokenPipe(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() {
|
||||
Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => {
|
||||
return Err(Error::BrokenPipe(e));
|
||||
return Err(Error::Slirp(SlirpError::BrokenPipe(e)));
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(Error::OverlappedError(e));
|
||||
return Err(Error::Slirp(SlirpError::OverlappedError(e)));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,9 @@ sudo apt-get install --yes --no-install-recommends \
|
|||
libdbus-1-dev \
|
||||
libdrm-dev \
|
||||
libepoxy-dev \
|
||||
libglib2.0-dev \
|
||||
libguestfs-tools \
|
||||
libslirp-dev \
|
||||
libssl-dev \
|
||||
libswscale-dev \
|
||||
libudev-dev \
|
||||
|
|
Loading…
Reference in a new issue