diff options
author | Chirantan Ekbote <chirantan@chromium.org> | 2018-04-16 19:32:04 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-08-08 13:45:44 -0700 |
commit | ebd56813e189de4a2d2bad2699bc07b56e580035 (patch) | |
tree | a890b6c34c5c3cc36864560cc22f0ab84c3f5ecb /src | |
parent | a79073ad7d244b45bca5ba0c5ddf92b04827fc24 (diff) | |
download | crosvm-ebd56813e189de4a2d2bad2699bc07b56e580035.tar crosvm-ebd56813e189de4a2d2bad2699bc07b56e580035.tar.gz crosvm-ebd56813e189de4a2d2bad2699bc07b56e580035.tar.bz2 crosvm-ebd56813e189de4a2d2bad2699bc07b56e580035.tar.lz crosvm-ebd56813e189de4a2d2bad2699bc07b56e580035.tar.xz crosvm-ebd56813e189de4a2d2bad2699bc07b56e580035.tar.zst crosvm-ebd56813e189de4a2d2bad2699bc07b56e580035.zip |
virtio: Implement the 9P device
Implement a new virtio_9p device to be used for sharing directories with the VM. BUG=chromium:703939 TEST=mount inside a VM and run `bonnie++ -r 256` Append the shared directory to the crosvm command line: --shared-dir /path/to/dir:test_9p Then mount in the guest: mkdir /tmp/9p mount -t 9p -o trans=virtio test_9p /tmp/9p -oversion=9p2000.L Or for a 9p root: run --shared-dir /mnt/vm_root:/dev/root -p 'root=/dev/root ro rootflags=ro,trans=virtio,version=9p2000.L,cache=loose rootfstype=9p' vmlinux.bin CQ-DEPEND=CL:1065170 Change-Id: I41fc21306ab5fa318a271f172d7057b767b29f31 Signed-off-by: Chirantan Ekbote <chirantan@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1065173 Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com> Reviewed-by: Dylan Reid <dgreid@chromium.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/linux.rs | 50 | ||||
-rw-r--r-- | src/main.rs | 32 |
2 files changed, 81 insertions, 1 deletions
diff --git a/src/linux.rs b/src/linux.rs index 191de34..98d0c5e 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -74,6 +74,7 @@ pub enum Error { NetDeviceNew(devices::virtio::NetError), NoVarEmpty, OpenKernel(PathBuf, io::Error), + P9DeviceNew(devices::virtio::P9Error), PollContextAdd(sys_util::Error), PollContextDelete(sys_util::Error), QcowDeviceCreate(qcow::Error), @@ -83,6 +84,7 @@ pub enum Error { RegisterBlock(MmioRegisterError), RegisterGpu(MmioRegisterError), RegisterNet(MmioRegisterError), + RegisterP9(MmioRegisterError), RegisterRng(MmioRegisterError), RegisterSignalHandler(sys_util::Error), RegisterVsock(MmioRegisterError), @@ -139,6 +141,7 @@ impl fmt::Display for Error { &Error::OpenKernel(ref p, ref e) => { write!(f, "failed to open kernel image {:?}: {}", p, e) } + &Error::P9DeviceNew(ref e) => write!(f, "failed to create 9p device: {}", e), &Error::PollContextAdd(ref e) => write!(f, "failed to add fd to poll context: {:?}", e), &Error::PollContextDelete(ref e) => { write!(f, "failed to remove fd from poll context: {:?}", e) @@ -158,6 +161,7 @@ impl fmt::Display for Error { &Error::RegisterBlock(ref e) => write!(f, "error registering block device: {:?}", e), &Error::RegisterGpu(ref e) => write!(f, "error registering gpu device: {:?}", e), &Error::RegisterNet(ref e) => write!(f, "error registering net device: {:?}", e), + &Error::RegisterP9(ref e) => write!(f, "error registering 9p device: {:?}", e), &Error::RegisterRng(ref e) => write!(f, "error registering rng device: {:?}", e), &Error::RegisterSignalHandler(ref e) => { write!(f, "error registering signal handler: {:?}", e) @@ -602,6 +606,52 @@ fn setup_mmio_bus(cfg: &Config, } } + let chronos_user_group = CStr::from_bytes_with_nul(b"chronos\0").unwrap(); + let chronos_uid = match get_user_id(&chronos_user_group) { + Ok(u) => u, + Err(e) => { + warn!("falling back to current user id for 9p: {:?}", e); + geteuid() + } + }; + let chronos_gid = match get_group_id(&chronos_user_group) { + Ok(u) => u, + Err(e) => { + warn!("falling back to current group id for 9p: {:?}", e); + getegid() + } + }; + + for &(ref src, ref tag) in &cfg.shared_dirs { + let (jail, root) = if cfg.multiprocess { + let policy_path: PathBuf = cfg.seccomp_policy_dir.join("9p_device.policy"); + let mut jail = create_base_minijail(empty_root_path, &policy_path)?; + + // The shared directory becomes the root of the device's file system. + let root = Path::new("/"); + jail.mount_bind(&src, root, true).unwrap(); + + // Set the uid/gid for the jailed process, and give a basic id map. This + // is required for the above bind mount to work. + jail.change_uid(chronos_uid); + jail.change_gid(chronos_gid); + jail.uidmap(&format!("{0} {0} 1", chronos_uid)) + .map_err(Error::SettingUidMap)?; + jail.gidmap(&format!("{0} {0} 1", chronos_gid)) + .map_err(Error::SettingGidMap)?; + + (Some(jail), root) + } else { + // There's no bind mount so we tell the server to treat the source directory as the + // root. The double deref here converts |src| from a &PathBuf into a &Path. + (None, &**src) + }; + + let p9_box = Box::new(devices::virtio::P9::new(root, tag).map_err(Error::P9DeviceNew)?); + + register_mmio(&mut bus, vm, p9_box, jail, resources, cmdline).map_err(Error::RegisterP9)?; + } + Ok(bus) } diff --git a/src/main.rs b/src/main.rs index c85d720..d1de672 100644 --- a/src/main.rs +++ b/src/main.rs @@ -80,6 +80,7 @@ pub struct Config { wayland_socket_path: Option<PathBuf>, wayland_dmabuf: bool, socket_path: Option<PathBuf>, + shared_dirs: Vec<(PathBuf, String)>, multiprocess: bool, seccomp_policy_dir: PathBuf, cid: Option<u64>, @@ -105,6 +106,7 @@ impl Default for Config { wayland_dmabuf: false, socket_path: None, multiprocess: !cfg!(feature = "default-no-sandbox"), + shared_dirs: Vec::new(), seccomp_policy_dir: PathBuf::from(SECCOMP_POLICY_DIR), cid: None, gpu: false, @@ -318,6 +320,32 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument:: } })?); } + "shared-dir" => { + // Formatted as <src:tag>. + let param = value.unwrap(); + let mut components = param.splitn(2, ':'); + let src = PathBuf::from(components.next().ok_or_else(|| { + argument::Error::InvalidValue { + value: param.to_owned(), + expected: "missing source path for `shared-dir`", + } + })?); + let tag = components.next().ok_or_else(|| { + argument::Error::InvalidValue { + value: param.to_owned(), + expected: "missing tag for `shared-dir`", + } + })?.to_owned(); + + if !src.is_dir() { + return Err(argument::Error::InvalidValue { + value: param.to_owned(), + expected: "source path for `shared-dir` must be a directory", + }); + } + + cfg.shared_dirs.push((src, tag)); + } "seccomp-policy-dir" => { // `value` is Some because we are in this match so it's safe to unwrap. cfg.seccomp_policy_dir = PathBuf::from(value.unwrap()); @@ -401,7 +429,9 @@ fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> { "Path to put the control socket. If PATH is a directory, a name will be generated."), Argument::short_flag('u', "multiprocess", "Run each device in a child process(default)."), Argument::flag("disable-sandbox", "Run all devices in one, non-sandboxed process."), - Argument::value("cid", "CID", "Context ID for virtual sockets"), + Argument::value("cid", "CID", "Context ID for virtual sockets."), + Argument::value("shared-dir", "PATH:TAG", + "Directory to be shared with a VM as a source:tag pair. Can be given more than once."), Argument::value("seccomp-policy-dir", "PATH", "Path to seccomp .policy files."), #[cfg(feature = "plugin")] Argument::value("plugin", "PATH", "Absolute path to plugin process to run under crosvm."), |