summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2020-05-08 15:27:56 +0000
committerAlyssa Ross <hi@alyssa.is>2020-05-10 02:39:28 +0000
commit2f8d50adc97cc7fca6f710bd575b4f71ccb40f6b (patch)
treefefaf2c13796f8f2fa9a13b99b09c3b40ab5966b /src
parent00c41c28bbc44b37fc8dcf5d2a5b4679f2aa4297 (diff)
parent03a54abf852984f696e7a101ff9590f05ebcba5b (diff)
downloadcrosvm-2f8d50adc97cc7fca6f710bd575b4f71ccb40f6b.tar
crosvm-2f8d50adc97cc7fca6f710bd575b4f71ccb40f6b.tar.gz
crosvm-2f8d50adc97cc7fca6f710bd575b4f71ccb40f6b.tar.bz2
crosvm-2f8d50adc97cc7fca6f710bd575b4f71ccb40f6b.tar.lz
crosvm-2f8d50adc97cc7fca6f710bd575b4f71ccb40f6b.tar.xz
crosvm-2f8d50adc97cc7fca6f710bd575b4f71ccb40f6b.tar.zst
crosvm-2f8d50adc97cc7fca6f710bd575b4f71ccb40f6b.zip
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'src')
-rw-r--r--src/argument.rs10
-rw-r--r--src/crosvm.rs6
-rw-r--r--src/linux.rs64
-rw-r--r--src/main.rs145
-rw-r--r--src/plugin/mod.rs6
-rw-r--r--src/plugin/process.rs19
-rw-r--r--src/plugin/vcpu.rs1
7 files changed, 175 insertions, 76 deletions
diff --git a/src/argument.rs b/src/argument.rs
index 0d0e142..26950a9 100644
--- a/src/argument.rs
+++ b/src/argument.rs
@@ -292,14 +292,12 @@ where
                     State::Positional
                 }
                 State::Value { name } => {
-                    if arg.starts_with("-") {
+                    if arg.starts_with('-') {
                         arg_consumed = false;
                         f(&name, None)?;
-                    } else {
-                        if let Err(e) = f(&name, Some(&arg)) {
-                            arg_consumed = false;
-                            f(&name, None).map_err(|_| e)?;
-                        }
+                    } else if let Err(e) = f(&name, Some(&arg)) {
+                        arg_consumed = false;
+                        f(&name, None).map_err(|_| e)?;
                     }
                     State::Top
                 }
diff --git a/src/crosvm.rs b/src/crosvm.rs
index a55d2e4..49a08c0 100644
--- a/src/crosvm.rs
+++ b/src/crosvm.rs
@@ -16,11 +16,11 @@ use std::os::unix::io::RawFd;
 use std::path::{Path, PathBuf};
 use std::str::FromStr;
 
-use arch::Pstore;
+use arch::{Pstore, SerialHardware, SerialParameters};
 use devices::virtio::fs::passthrough;
 #[cfg(feature = "gpu")]
 use devices::virtio::gpu::GpuParameters;
-use devices::{Ac97Parameters, SerialParameters};
+use devices::Ac97Parameters;
 use libc::{getegid, geteuid};
 
 static SECCOMP_POLICY_DIR: &str = "/usr/share/policy/crosvm";
@@ -193,7 +193,7 @@ pub struct Config {
     pub display_window_keyboard: bool,
     pub display_window_mouse: bool,
     pub ac97_parameters: Vec<Ac97Parameters>,
-    pub serial_parameters: BTreeMap<u8, SerialParameters>,
+    pub serial_parameters: BTreeMap<(SerialHardware, u8), SerialParameters>,
     pub syslog_tag: Option<String>,
     pub virtio_single_touch: Option<TouchDeviceOption>,
     pub virtio_trackpad: Option<TouchDeviceOption>,
diff --git a/src/linux.rs b/src/linux.rs
index 9dbdb5c..e480a4c 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use std;
 use std::cmp::max;
 use std::convert::TryFrom;
 use std::error::Error as StdError;
@@ -29,7 +28,7 @@ use libc::{self, c_int, gid_t, uid_t};
 
 #[cfg(feature = "gpu")]
 use devices::virtio::EventDevice;
-use devices::virtio::{self, VirtioDevice};
+use devices::virtio::{self, Console, VirtioDevice};
 use devices::{
     self, Ac97Backend, Ac97Dev, HostBackendDeviceProvider, PciDevice, VfioContainer, VfioDevice,
     VfioPciDevice, VirtioPciDevice, XhciController,
@@ -50,7 +49,6 @@ use sys_util::{
     Killable, MemoryMappingArena, PollContext, PollToken, Protection, ScopedEvent, SignalFd,
     Terminal, TimerFd, WatchingEvents, SIGRTMIN,
 };
-use vhost;
 use vm_control::{
     BalloonControlCommand, BalloonControlRequestSocket, BalloonControlResponseSocket,
     BalloonControlResult, DiskControlCommand, DiskControlRequestSocket, DiskControlResponseSocket,
@@ -61,7 +59,10 @@ use vm_control::{
 };
 
 use crate::{Config, DiskOption, Executable, SharedDir, SharedDirKind, TouchDeviceOption};
-use arch::{self, LinuxArch, RunnableLinuxVm, VirtioDeviceStub, VmComponents, VmImage};
+use arch::{
+    self, LinuxArch, RunnableLinuxVm, SerialHardware, SerialParameters, VirtioDeviceStub,
+    VmComponents, VmImage,
+};
 
 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
 use aarch64::AArch64 as Arch;
@@ -82,6 +83,7 @@ pub enum Error {
     ChownTpmStorage(sys_util::Error),
     CloneEventFd(sys_util::Error),
     CreateAc97(devices::PciDeviceError),
+    CreateConsole(arch::serial::Error),
     CreateDiskError(disk::Error),
     CreateEventFd(sys_util::Error),
     CreatePollContext(sys_util::Error),
@@ -166,6 +168,7 @@ impl Display for Error {
             ChownTpmStorage(e) => write!(f, "failed to chown tpm storage: {}", e),
             CloneEventFd(e) => write!(f, "failed to clone eventfd: {}", e),
             CreateAc97(e) => write!(f, "failed to create ac97 device: {}", e),
+            CreateConsole(e) => write!(f, "failed to create console device: {}", e),
             CreateDiskError(e) => write!(f, "failed to create virtual disk: {}", e),
             CreateEventFd(e) => write!(f, "failed to create eventfd: {}", e),
             CreatePollContext(e) => write!(f, "failed to create poll context: {}", e),
@@ -816,7 +819,12 @@ fn create_fs_device(
             log_failures: cfg.seccomp_log_failures,
             seccomp_policy: &seccomp_policy,
         };
-        create_base_minijail(src, Some(max_open_files), Some(&config))?
+        let mut jail = create_base_minijail(src, Some(max_open_files), Some(&config))?;
+        // We want bind mounts from the parent namespaces to propagate into the fs device's
+        // namespace.
+        jail.set_remount_mode(libc::MS_SLAVE);
+
+        jail
     } else {
         create_base_minijail(src, Some(max_open_files), None)?
     };
@@ -885,7 +893,7 @@ fn create_pmem_device(
         .open(&disk.path)
         .map_err(|e| Error::Disk(disk.path.to_path_buf(), e))?;
 
-    let (disk_size, arena_size) = {
+    let arena_size = {
         let metadata =
             std::fs::metadata(&disk.path).map_err(|e| Error::Disk(disk.path.to_path_buf(), e))?;
         let disk_len = metadata.len();
@@ -900,12 +908,9 @@ fn create_pmem_device(
         } else {
             0
         };
-        (
-            disk_len,
-            disk_len
-                .checked_add(align_adjust)
-                .ok_or(Error::PmemDeviceImageTooBig)?,
-        )
+        disk_len
+            .checked_add(align_adjust)
+            .ok_or(Error::PmemDeviceImageTooBig)?
     };
 
     let protection = {
@@ -919,11 +924,10 @@ fn create_pmem_device(
     let arena = {
         // Conversion from u64 to usize may fail on 32bit system.
         let arena_size = usize::try_from(arena_size).map_err(|_| Error::PmemDeviceImageTooBig)?;
-        let disk_size = usize::try_from(disk_size).map_err(|_| Error::PmemDeviceImageTooBig)?;
 
         let mut arena = MemoryMappingArena::new(arena_size).map_err(Error::ReservePmemMemory)?;
         arena
-            .add_fd_offset_protection(0, disk_size, &fd, 0, protection)
+            .add_fd_offset_protection(0, arena_size, &fd, 0, protection)
             .map_err(Error::ReservePmemMemory)?;
         arena
     };
@@ -963,6 +967,19 @@ fn create_pmem_device(
     })
 }
 
+fn create_console_device(cfg: &Config, param: &SerialParameters) -> DeviceResult {
+    let mut keep_fds = Vec::new();
+    let evt = EventFd::new().map_err(Error::CreateEventFd)?;
+    let dev = param
+        .create_serial_device::<Console>(&evt, &mut keep_fds)
+        .map_err(Error::CreateConsole)?;
+
+    Ok(VirtioDeviceStub {
+        dev: Box::new(dev),
+        jail: simple_jail(&cfg, "serial")?, // TODO(dverkamp): use a separate policy for console?
+    })
+}
+
 // gpu_device_socket is not used when GPU support is disabled.
 #[cfg_attr(not(feature = "gpu"), allow(unused_variables))]
 fn create_virtio_devices(
@@ -979,6 +996,15 @@ fn create_virtio_devices(
 ) -> DeviceResult<Vec<VirtioDeviceStub>> {
     let mut devs = Vec::new();
 
+    for (_, param) in cfg
+        .serial_parameters
+        .iter()
+        .filter(|(_k, v)| v.hardware == SerialHardware::VirtioConsole)
+    {
+        let dev = create_console_device(cfg, param)?;
+        devs.push(dev);
+    }
+
     for disk in &cfg.disks {
         let disk_device_socket = disk_device_sockets.remove(0);
         devs.push(create_block_device(cfg, disk, disk_device_socket)?);
@@ -1398,7 +1424,7 @@ fn run_vcpu(
             // implementation accomplishes that.
             let _scoped_exit_evt = ScopedEvent::from(exit_evt);
 
-            if vcpu_affinity.len() != 0 {
+            if !vcpu_affinity.is_empty() {
                 if let Err(e) = set_cpu_affinity(vcpu_affinity) {
                     error!("Failed to set CPU affinity: {}", e);
                 }
@@ -1740,7 +1766,7 @@ fn run_control(
     }
 
     // Balance available memory between guest and host every second.
-    let mut balancemem_timer = TimerFd::new().map_err(Error::CreateTimerFd)?;
+    let balancemem_timer = TimerFd::new().map_err(Error::CreateTimerFd)?;
     if Path::new(LOWMEM_AVAILABLE).exists() {
         // Create timer request balloon stats every 1s.
         poll_ctx
@@ -2024,7 +2050,7 @@ fn run_control(
                                     }
                                 }
                                 Err(e) => {
-                                    if let MsgError::BadRecvSize { actual: 0, .. } = e {
+                                    if let MsgError::RecvZero = e {
                                         vm_control_indices_to_remove.push(index);
                                     } else {
                                         error!("failed to recv VmRequest: {}", e);
@@ -2040,7 +2066,7 @@ fn run_control(
                                     }
                                 }
                                 Err(e) => {
-                                    if let MsgError::BadRecvSize { actual: 0, .. } = e {
+                                    if let MsgError::RecvZero = e {
                                         vm_control_indices_to_remove.push(index);
                                     } else {
                                         error!("failed to recv VmMemoryControlRequest: {}", e);
@@ -2056,7 +2082,7 @@ fn run_control(
                                     }
                                 }
                                 Err(e) => {
-                                    if let MsgError::BadRecvSize { actual: 0, .. } = e {
+                                    if let MsgError::RecvZero = e {
                                         vm_control_indices_to_remove.push(index);
                                     } else {
                                         error!("failed to recv VmIrqRequest: {}", e);
diff --git a/src/main.rs b/src/main.rs
index e33be66..7fd3eca 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -17,7 +17,7 @@ use std::string::String;
 use std::thread::sleep;
 use std::time::Duration;
 
-use arch::Pstore;
+use arch::{set_default_serial_parameters, Pstore, SerialHardware, SerialParameters, SerialType};
 use audio_streams::StreamEffect;
 use crosvm::{
     argument::{self, print_help, set_arguments, Argument},
@@ -25,7 +25,7 @@ use crosvm::{
 };
 #[cfg(feature = "gpu")]
 use devices::virtio::gpu::{GpuMode, GpuParameters};
-use devices::{Ac97Backend, Ac97Parameters, SerialParameters, SerialType};
+use devices::{Ac97Backend, Ac97Parameters};
 use disk::QcowFile;
 use msg_socket::{MsgReceiver, MsgSender, MsgSocket};
 use sys_util::{
@@ -74,7 +74,7 @@ fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> {
     let mut cpuset = Vec::new();
     for part in s.split(',') {
         let range: Vec<&str> = part.split('-').collect();
-        if range.len() == 0 || range.len() > 2 {
+        if range.is_empty() || range.len() > 2 {
             return Err(argument::Error::InvalidValue {
                 value: part.to_owned(),
                 expected: String::from("invalid list syntax"),
@@ -117,8 +117,8 @@ fn parse_gpu_options(s: Option<&str>) -> argument::Result<GpuParameters> {
 
     if let Some(s) = s {
         let opts = s
-            .split(",")
-            .map(|frag| frag.split("="))
+            .split(',')
+            .map(|frag| frag.split('='))
             .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
 
         for (k, v) in opts {
@@ -252,8 +252,8 @@ fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
     let mut ac97_params: Ac97Parameters = Default::default();
 
     let opts = s
-        .split(",")
-        .map(|frag| frag.split("="))
+        .split(',')
+        .map(|frag| frag.split('='))
         .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
 
     for (k, v) in opts {
@@ -273,7 +273,7 @@ fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
             }
             "capture_effects" => {
                 ac97_params.capture_effects = v
-                    .split("|")
+                    .split('|')
                     .map(|val| {
                         val.parse::<StreamEffect>()
                             .map_err(|e| argument::Error::InvalidValue {
@@ -298,19 +298,27 @@ fn parse_ac97_options(s: &str) -> argument::Result<Ac97Parameters> {
 fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
     let mut serial_setting = SerialParameters {
         type_: SerialType::Sink,
+        hardware: SerialHardware::Serial,
         path: None,
+        input: None,
         num: 1,
         console: false,
+        earlycon: false,
         stdin: false,
     };
 
     let opts = s
-        .split(",")
-        .map(|frag| frag.split("="))
+        .split(',')
+        .map(|frag| frag.split('='))
         .map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
 
     for (k, v) in opts {
         match k {
+            "hardware" => {
+                serial_setting.hardware = v
+                    .parse::<SerialHardware>()
+                    .map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
+            }
             "type" => {
                 serial_setting.type_ = v
                     .parse::<SerialType>()
@@ -336,12 +344,33 @@ fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
                     ))
                 })?
             }
+            "earlycon" => {
+                serial_setting.earlycon = v.parse::<bool>().map_err(|e| {
+                    argument::Error::Syntax(format!(
+                        "serial device earlycon is not parseable: {}",
+                        e,
+                    ))
+                })?
+            }
             "stdin" => {
                 serial_setting.stdin = v.parse::<bool>().map_err(|e| {
                     argument::Error::Syntax(format!("serial device stdin is not parseable: {}", e))
-                })?
+                })?;
+                if serial_setting.stdin && serial_setting.input.is_some() {
+                    return Err(argument::Error::TooManyArguments(
+                        "Cannot specify both stdin and input options".to_string(),
+                    ));
+                }
             }
             "path" => serial_setting.path = Some(PathBuf::from(v)),
+            "input" => {
+                if serial_setting.stdin {
+                    return Err(argument::Error::TooManyArguments(
+                        "Cannot specify both stdin and input options".to_string(),
+                    ));
+                }
+                serial_setting.input = Some(PathBuf::from(v));
+            }
             _ => {
                 return Err(argument::Error::UnknownArgument(format!(
                     "serial parameter {}",
@@ -355,7 +384,7 @@ fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
 }
 
 fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
-    let components: Vec<&str> = value.split(":").collect();
+    let components: Vec<&str> = value.split(':').collect();
     if components.is_empty() || components.len() > 3 || components[0].is_empty() {
         return Err(argument::Error::InvalidValue {
             value: value.to_owned(),
@@ -402,7 +431,7 @@ fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
 }
 
 fn parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap> {
-    let components: Vec<&str> = value.split(":").collect();
+    let components: Vec<&str> = value.split(':').collect();
     if components.is_empty() || components.len() > 3 || components[0].is_empty() {
         return Err(argument::Error::InvalidValue {
             value: value.to_owned(),
@@ -501,7 +530,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                 )
         }
         "cpu-affinity" => {
-            if cfg.vcpu_affinity.len() != 0 {
+            if !cfg.vcpu_affinity.is_empty() {
                 return Err(argument::Error::TooManyArguments(
                     "`cpu-affinity` already given".to_owned(),
                 ));
@@ -532,10 +561,11 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
         "serial" => {
             let serial_params = parse_serial_options(value.unwrap())?;
             let num = serial_params.num;
-            if cfg.serial_parameters.contains_key(&num) {
+            let key = (serial_params.hardware, num);
+            if cfg.serial_parameters.contains_key(&key) {
                 return Err(argument::Error::TooManyArguments(format!(
-                    "serial num {}",
-                    num
+                    "serial hardware {} num {}",
+                    serial_params.hardware, num,
                 )));
             }
 
@@ -543,8 +573,29 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                 for params in cfg.serial_parameters.values() {
                     if params.console {
                         return Err(argument::Error::TooManyArguments(format!(
-                            "serial device {} already set as console",
-                            params.num
+                            "{} device {} already set as console",
+                            params.hardware, params.num,
+                        )));
+                    }
+                }
+            }
+
+            if serial_params.earlycon {
+                // Only SerialHardware::Serial supports earlycon= currently.
+                match serial_params.hardware {
+                    SerialHardware::Serial => {}
+                    _ => {
+                        return Err(argument::Error::InvalidValue {
+                            value: serial_params.hardware.to_string().to_owned(),
+                            expected: String::from("earlycon not supported for hardware"),
+                        });
+                    }
+                }
+                for params in cfg.serial_parameters.values() {
+                    if params.earlycon {
+                        return Err(argument::Error::TooManyArguments(format!(
+                            "{} device {} already set as earlycon",
+                            params.hardware, params.num,
                         )));
                     }
                 }
@@ -553,13 +604,13 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             if serial_params.stdin {
                 if let Some(previous_stdin) = cfg.serial_parameters.values().find(|sp| sp.stdin) {
                     return Err(argument::Error::TooManyArguments(format!(
-                        "serial device {} already connected to standard input",
-                        previous_stdin.num
+                        "{} device {} already connected to standard input",
+                        previous_stdin.hardware, previous_stdin.num,
                     )));
                 }
             }
 
-            cfg.serial_parameters.insert(num, serial_params);
+            cfg.serial_parameters.insert(key, serial_params);
         }
         "syslog-tag" => {
             if cfg.syslog_tag.is_some() {
@@ -1030,7 +1081,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             let reader = BufReader::new(file);
             for l in reader.lines() {
                 let line = l.unwrap();
-                let trimmed_line = line.splitn(2, '#').nth(0).unwrap().trim();
+                let trimmed_line = line.splitn(2, '#').next().unwrap().trim();
                 if !trimmed_line.is_empty() {
                     let mount = parse_plugin_mount_option(trimmed_line)?;
                     cfg.plugin_mounts.push(mount);
@@ -1049,7 +1100,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
             let reader = BufReader::new(file);
             for l in reader.lines() {
                 let line = l.unwrap();
-                let trimmed_line = line.splitn(2, '#').nth(0).unwrap().trim();
+                let trimmed_line = line.splitn(2, '#').next().unwrap().trim();
                 if !trimmed_line.is_empty() {
                     let map = parse_plugin_gid_map_option(trimmed_line)?;
                     cfg.plugin_gid_maps.push(map);
@@ -1084,7 +1135,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                     "`single-touch` already given".to_owned(),
                 ));
             }
-            let mut it = value.unwrap().split(":");
+            let mut it = value.unwrap().split(':');
 
             let mut single_touch_spec =
                 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
@@ -1102,7 +1153,7 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
                     "`trackpad` already given".to_owned(),
                 ));
             }
-            let mut it = value.unwrap().split(":");
+            let mut it = value.unwrap().split(':');
 
             let mut trackpad_spec =
                 TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned()));
@@ -1214,6 +1265,7 @@ fn validate_arguments(cfg: &mut Config) -> std::result::Result<(), argument::Err
             }
         }
     }
+    set_default_serial_parameters(&mut cfg.serial_parameters);
     Ok(())
 }
 
@@ -1326,10 +1378,9 @@ Possible key values:
     effect value now is EchoCancellation or aec.
 "#,
         ),
-
         Argument::value(
             "serial",
-            "type=TYPE,[num=NUM,path=PATH,console,stdin]",
+            "type=TYPE,[hardware=HW,num=NUM,path=PATH,input=PATH,console,earlycon,stdin]",
             "\
 Comma separated key=value pairs for setting up serial devices. Can be given
 more than once.
@@ -1339,16 +1390,26 @@ Possible key values:
   type=(stdout,syslog,sink,file)
     Where to route the serial device.
 
+  hardware=(serial,virtio-console)
+    Which type of serial hardware to emulate.  Defaults to 8250 UART
+    (serial).
+
   num=(1,2,3,4)
     Serial Device Number.  If not provided, num will default to 1.
 
   path=PATH
     The path to the file to write to when type=file.
 
+  input=PATH
+    The path to the file to read from when not stdin.
+
   console
     Use this serial device as the guest console.  Can only be given once.
     Will default to first serial port if not provided.
 
+  earlycon
+    Use this serial device as the early console.  Can only be given once.
+
   stdin
     Direct standard input to this serial device.  Can only be given once.
     Will default to first serial port if not provided.
@@ -1738,7 +1799,7 @@ fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
         println!("Set the balloon size of the crosvm instance to `SIZE` bytes.");
         return Err(());
     }
-    let num_bytes = match args.nth(0).unwrap().parse::<u64>() {
+    let num_bytes = match args.next().unwrap().parse::<u64>() {
         Ok(n) => n,
         Err(_) => {
             error!("Failed to parse number of bytes");
@@ -1750,6 +1811,19 @@ fn balloon_vms(mut args: std::env::Args) -> std::result::Result<(), ()> {
     vms_request(&VmRequest::BalloonCommand(command), args)
 }
 
+fn balloon_stats(args: std::env::Args) -> std::result::Result<(), ()> {
+    if args.len() != 1 {
+        print_help("crosvm balloon_stats", "VM_SOCKET", &[]);
+        println!("Prints virtio balloon statistics for a `VM_SOCKET`.");
+        return Err(());
+    }
+    let command = BalloonControlCommand::Stats {};
+    let request = &VmRequest::BalloonCommand(command);
+    let response = handle_request(request, args)?;
+    println!("{}", response);
+    Ok(())
+}
+
 fn create_qcow2(args: std::env::Args) -> std::result::Result<(), ()> {
     let arguments = [
         Argument::positional("PATH", "where to create the qcow2 image"),
@@ -1796,7 +1870,7 @@ fn create_qcow2(args: std::env::Args) -> std::result::Result<(), ()> {
     .map_err(|e| {
         error!("Unable to parse command line arguments: {}", e);
     })?;
-    if file_path.len() == 0 || !(size.is_some() ^ backing_file.is_some()) {
+    if file_path.is_empty() || !(size.is_some() ^ backing_file.is_some()) {
         print_help("crosvm create_qcow2", "PATH [SIZE]", &arguments);
         println!(
             "Create a new QCOW2 image at `PATH` of either the specified `SIZE` in bytes or
@@ -1837,11 +1911,11 @@ fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
         println!("  resize DISK_INDEX NEW_SIZE VM_SOCKET");
         return Err(());
     }
-    let subcommand: &str = &args.nth(0).unwrap();
+    let subcommand: &str = &args.next().unwrap();
 
     let request = match subcommand {
         "resize" => {
-            let disk_index = match args.nth(0).unwrap().parse::<usize>() {
+            let disk_index = match args.next().unwrap().parse::<usize>() {
                 Ok(n) => n,
                 Err(_) => {
                     error!("Failed to parse disk index");
@@ -1849,7 +1923,7 @@ fn disk_cmd(mut args: std::env::Args) -> std::result::Result<(), ()> {
                 }
             };
 
-            let new_size = match args.nth(0).unwrap().parse::<u64>() {
+            let new_size = match args.next().unwrap().parse::<u64>() {
                 Ok(n) => n,
                 Err(_) => {
                     error!("Failed to parse disk size");
@@ -1911,7 +1985,7 @@ type ModifyUsbResult<T> = std::result::Result<T, ModifyUsbError>;
 
 fn parse_bus_id_addr(v: &str) -> ModifyUsbResult<(u8, u8, u16, u16)> {
     debug!("parse_bus_id_addr: {}", v);
-    let mut ids = v.split(":");
+    let mut ids = v.split(':');
     match (ids.next(), ids.next(), ids.next(), ids.next()) {
         (Some(bus_id), Some(addr), Some(vid), Some(pid)) => {
             let bus_id = bus_id
@@ -2069,7 +2143,7 @@ fn pkg_version() -> std::result::Result<(), ()> {
     print!("crosvm {}", VERSION.unwrap_or("UNKNOWN"));
     match PKG_VERSION {
         Some(v) => println!("-{}", v),
-        None => println!(""),
+        None => println!(),
     }
     Ok(())
 }
@@ -2099,6 +2173,7 @@ fn crosvm_main() -> std::result::Result<(), ()> {
         Some("resume") => resume_vms(args),
         Some("run") => run_vm(args),
         Some("balloon") => balloon_vms(args),
+        Some("balloon_stats") => balloon_stats(args),
         Some("create_qcow2") => create_qcow2(args),
         Some("disk") => disk_cmd(args),
         Some("usb") => modify_usb(args),
diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs
index ae7e19c..470d5f0 100644
--- a/src/plugin/mod.rs
+++ b/src/plugin/mod.rs
@@ -8,7 +8,7 @@ mod vcpu;
 use std::fmt::{self, Display};
 use std::fs::File;
 use std::io;
-use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
+use std::os::unix::io::{AsRawFd, FromRawFd};
 use std::os::unix::net::UnixDatagram;
 use std::path::Path;
 use std::result;
@@ -181,10 +181,6 @@ impl Display for Error {
 
 type Result<T> = result::Result<T, Error>;
 
-fn downcast_file<F: IntoRawFd>(f: F) -> File {
-    unsafe { File::from_raw_fd(f.into_raw_fd()) }
-}
-
 fn new_seqpacket_pair() -> SysResult<(UnixDatagram, UnixDatagram)> {
     let mut fds = [0, 0];
     unsafe {
diff --git a/src/plugin/process.rs b/src/plugin/process.rs
index 51fc892..783239a 100644
--- a/src/plugin/process.rs
+++ b/src/plugin/process.rs
@@ -7,19 +7,17 @@ use std::env::set_var;
 use std::fs::File;
 use std::io::Write;
 use std::mem::transmute;
-use std::os::unix::io::{AsRawFd, RawFd};
+use std::os::unix::io::{IntoRawFd, RawFd};
 use std::os::unix::net::UnixDatagram;
 use std::path::Path;
 use std::process::Command;
 use std::sync::{Arc, RwLock};
 use std::thread::JoinHandle;
 
-use net_util;
 use net_util::Error as NetError;
 
 use libc::{pid_t, waitpid, EINVAL, ENODATA, ENOTTY, WEXITSTATUS, WIFEXITED, WNOHANG, WTERMSIG};
 
-use protobuf;
 use protobuf::Message;
 
 use io_jail::Minijail;
@@ -348,7 +346,7 @@ impl Process {
         read_only: bool,
         dirty_log: bool,
     ) -> SysResult<()> {
-        let shm = SharedMemory::from_raw_fd(memfd)?;
+        let shm = SharedMemory::from_file(memfd)?;
         // Checking the seals ensures the plugin process won't shrink the mmapped file, causing us
         // to SIGBUS in the future.
         let seals = shm.get_seals()?;
@@ -517,7 +515,14 @@ impl Process {
         let request = protobuf::parse_from_bytes::<MainRequest>(&self.request_buffer[..msg_size])
             .map_err(Error::DecodeRequest)?;
 
-        let mut response_files = Vec::new();
+        /// Use this to make it easier to stuff various kinds of File-like objects into the
+        /// `boxed_fds` list.
+        fn box_owned_fd<F: IntoRawFd + 'static>(f: F) -> Box<dyn IntoRawFd> {
+            Box::new(f)
+        }
+
+        // This vec is used to extend ownership of certain FDs until the end of this function.
+        let mut boxed_fds = Vec::new();
         let mut response_fds = Vec::new();
         let mut response = MainResponse::new();
         let res = if request.has_create() {
@@ -559,7 +564,7 @@ impl Process {
                                 Ok(()) => {
                                     response_fds.push(evt.as_raw_fd());
                                     response_fds.push(resample_evt.as_raw_fd());
-                                    response_files.push(downcast_file(resample_evt));
+                                    boxed_fds.push(box_owned_fd(resample_evt));
                                     entry.insert(PluginObject::IrqEvent {
                                         irq_id: irq_event.irq_id,
                                         evt,
@@ -588,7 +593,7 @@ impl Process {
                 Ok((request_socket, child_socket)) => {
                     self.request_sockets.push(request_socket);
                     response_fds.push(child_socket.as_raw_fd());
-                    response_files.push(downcast_file(child_socket));
+                    boxed_fds.push(box_owned_fd(child_socket));
                     Ok(())
                 }
                 Err(e) => Err(e),
diff --git a/src/plugin/vcpu.rs b/src/plugin/vcpu.rs
index a5bf73c..4bd3ea8 100644
--- a/src/plugin/vcpu.rs
+++ b/src/plugin/vcpu.rs
@@ -13,7 +13,6 @@ use std::sync::{Arc, RwLock};
 
 use libc::{EINVAL, ENOENT, ENOTTY, EPERM, EPIPE, EPROTO};
 
-use protobuf;
 use protobuf::Message;
 
 use assertions::const_assert;