summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorChirantan Ekbote <chirantan@chromium.org>2018-04-16 19:32:04 -0700
committerchrome-bot <chrome-bot@chromium.org>2018-08-08 13:45:44 -0700
commitebd56813e189de4a2d2bad2699bc07b56e580035 (patch)
treea890b6c34c5c3cc36864560cc22f0ab84c3f5ecb /src
parenta79073ad7d244b45bca5ba0c5ddf92b04827fc24 (diff)
downloadcrosvm-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.rs50
-rw-r--r--src/main.rs32
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."),