summary refs log tree commit diff
path: root/src/main.rs
diff options
context:
space:
mode:
authorDmitry Torokhov <dtor@chromium.org>2019-12-13 11:47:52 -0800
committerCommit Bot <commit-bot@chromium.org>2019-12-18 01:11:47 +0000
commit458bb643441bc122fe7b39e1c0f526b4d5bd976a (patch)
tree7974aa07e93cc89e2f11664dc003db5d41bde772 /src/main.rs
parent5aef6474862a94c85ac90757fe8ba7376a3ed1d6 (diff)
downloadcrosvm-458bb643441bc122fe7b39e1c0f526b4d5bd976a.tar
crosvm-458bb643441bc122fe7b39e1c0f526b4d5bd976a.tar.gz
crosvm-458bb643441bc122fe7b39e1c0f526b4d5bd976a.tar.bz2
crosvm-458bb643441bc122fe7b39e1c0f526b4d5bd976a.tar.lz
crosvm-458bb643441bc122fe7b39e1c0f526b4d5bd976a.tar.xz
crosvm-458bb643441bc122fe7b39e1c0f526b4d5bd976a.tar.zst
crosvm-458bb643441bc122fe7b39e1c0f526b4d5bd976a.zip
crosvm: allow shorthand notation in plugin-mount and plugin-gid-map
Let's allow shorthand notation in the form of:

	--plugin-mount=<src>[:[<dst>][:<writable>]]
	--plugin-gid-map=<inner>[:[<outer>][:<count>]]

so that we can invoke crosvm as

	crosvm ... --plugin-mount=/bin --plugin-mount=/dev/log::true \
		--plugin-gid-map=123 --plugin-gid-map=567::5

as repeating the data for both src and destination muddies the waters
and is prone to errors.

BUG=None
TEST=cargo test

Change-Id: I6f0a075ea3b27d4ec3dcf88698069930c158e759
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1967786
Reviewed-by: Zach Reizner <zachr@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Dmitry Torokhov <dtor@chromium.org>
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs129
1 files changed, 112 insertions, 17 deletions
diff --git a/src/main.rs b/src/main.rs
index 0e11ee1..c8dda2c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -270,10 +270,10 @@ 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();
-    if components.len() != 3 {
+    if components.is_empty() || components.len() > 3 || components[0].is_empty() {
         return Err(argument::Error::InvalidValue {
             value: value.to_owned(),
-            expected: "`plugin-mount` must have exactly 3 components: <src>:<dst>:<writable>",
+            expected: "`plugin-mount` should be in a form of: <src>[:[<dst>][:<writable>]]",
         });
     }
 
@@ -291,7 +291,10 @@ fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
         });
     }
 
-    let dst = PathBuf::from(components[1]);
+    let dst = PathBuf::from(match components.get(1) {
+        None | Some(&"") => components[0],
+        Some(path) => path,
+    });
     if dst.is_relative() {
         return Err(argument::Error::InvalidValue {
             value: components[1].to_owned(),
@@ -299,22 +302,24 @@ fn parse_plugin_mount_option(value: &str) -> argument::Result<BindMount> {
         });
     }
 
-    let writable: bool = components[2]
-        .parse()
-        .map_err(|_| argument::Error::InvalidValue {
+    let writable: bool = match components.get(2) {
+        None => false,
+        Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
             value: components[2].to_owned(),
             expected: "the <writable> component for `plugin-mount` is not valid bool",
-        })?;
+        })?,
+    };
 
     Ok(BindMount { src, dst, writable })
 }
 
 fn parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap> {
     let components: Vec<&str> = value.split(":").collect();
-    if components.len() != 3 {
+    if components.is_empty() || components.len() > 3 || components[0].is_empty() {
         return Err(argument::Error::InvalidValue {
             value: value.to_owned(),
-            expected: "`plugin-gid-map` must have exactly 3 components: <inner>:<outer>:<count>",
+            expected:
+                "`plugin-gid-map` must have exactly 3 components: <inner>[:[<outer>][:<count>]]",
         });
     }
 
@@ -325,19 +330,21 @@ fn parse_plugin_gid_map_option(value: &str) -> argument::Result<GidMap> {
             expected: "the <inner> component for `plugin-gid-map` is not valid gid",
         })?;
 
-    let outer: libc::gid_t = components[1]
-        .parse()
-        .map_err(|_| argument::Error::InvalidValue {
+    let outer: libc::gid_t = match components.get(1) {
+        None | Some(&"") => inner,
+        Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
             value: components[1].to_owned(),
             expected: "the <outer> component for `plugin-gid-map` is not valid gid",
-        })?;
+        })?,
+    };
 
-    let count: u32 = components[2]
-        .parse()
-        .map_err(|_| argument::Error::InvalidValue {
+    let count: u32 = match components.get(2) {
+        None => 1,
+        Some(s) => s.parse().map_err(|_| argument::Error::InvalidValue {
             value: components[2].to_owned(),
             expected: "the <count> component for `plugin-gid-map` is not valid number",
-        })?;
+        })?,
+    };
 
     Ok(GidMap {
         inner,
@@ -1672,4 +1679,92 @@ mod tests {
         set_argument(&mut config, "serial", Some("num=2,type=stdout,stdin=true"))
             .expect_err("should fail to parse a second serial port connected to stdin");
     }
+
+    #[test]
+    fn parse_plugin_mount_valid() {
+        let mut config = Config::default();
+        set_argument(
+            &mut config,
+            "plugin-mount",
+            Some("/dev/null:/dev/zero:true"),
+        )
+        .expect("parse should succeed");
+        assert_eq!(config.plugin_mounts[0].src, PathBuf::from("/dev/null"));
+        assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/zero"));
+        assert_eq!(config.plugin_mounts[0].writable, true);
+    }
+
+    #[test]
+    fn parse_plugin_mount_valid_shorthand() {
+        let mut config = Config::default();
+        set_argument(&mut config, "plugin-mount", Some("/dev/null")).expect("parse should succeed");
+        assert_eq!(config.plugin_mounts[0].dst, PathBuf::from("/dev/null"));
+        assert_eq!(config.plugin_mounts[0].writable, false);
+        set_argument(&mut config, "plugin-mount", Some("/dev/null:/dev/zero"))
+            .expect("parse should succeed");
+        assert_eq!(config.plugin_mounts[1].dst, PathBuf::from("/dev/zero"));
+        assert_eq!(config.plugin_mounts[1].writable, false);
+        set_argument(&mut config, "plugin-mount", Some("/dev/null::true"))
+            .expect("parse should succeed");
+        assert_eq!(config.plugin_mounts[2].dst, PathBuf::from("/dev/null"));
+        assert_eq!(config.plugin_mounts[2].writable, true);
+    }
+
+    #[test]
+    fn parse_plugin_mount_invalid() {
+        let mut config = Config::default();
+        set_argument(&mut config, "plugin-mount", Some("")).expect_err("parse should fail");
+        set_argument(
+            &mut config,
+            "plugin-mount",
+            Some("/dev/null:/dev/null:true:false"),
+        )
+        .expect_err("parse should fail because too many arguments");
+        set_argument(&mut config, "plugin-mount", Some("null:/dev/null:true"))
+            .expect_err("parse should fail because source is not absolute");
+        set_argument(&mut config, "plugin-mount", Some("/dev/null:null:true"))
+            .expect_err("parse should fail because source is not absolute");
+        set_argument(&mut config, "plugin-mount", Some("/dev/null:null:blah"))
+            .expect_err("parse should fail because flag is not boolean");
+    }
+
+    #[test]
+    fn parse_plugin_gid_map_valid() {
+        let mut config = Config::default();
+        set_argument(&mut config, "plugin-gid-map", Some("1:2:3")).expect("parse should succeed");
+        assert_eq!(config.plugin_gid_maps[0].inner, 1);
+        assert_eq!(config.plugin_gid_maps[0].outer, 2);
+        assert_eq!(config.plugin_gid_maps[0].count, 3);
+    }
+
+    #[test]
+    fn parse_plugin_gid_map_valid_shorthand() {
+        let mut config = Config::default();
+        set_argument(&mut config, "plugin-gid-map", Some("1")).expect("parse should succeed");
+        assert_eq!(config.plugin_gid_maps[0].inner, 1);
+        assert_eq!(config.plugin_gid_maps[0].outer, 1);
+        assert_eq!(config.plugin_gid_maps[0].count, 1);
+        set_argument(&mut config, "plugin-gid-map", Some("1:2")).expect("parse should succeed");
+        assert_eq!(config.plugin_gid_maps[1].inner, 1);
+        assert_eq!(config.plugin_gid_maps[1].outer, 2);
+        assert_eq!(config.plugin_gid_maps[1].count, 1);
+        set_argument(&mut config, "plugin-gid-map", Some("1::3")).expect("parse should succeed");
+        assert_eq!(config.plugin_gid_maps[2].inner, 1);
+        assert_eq!(config.plugin_gid_maps[2].outer, 1);
+        assert_eq!(config.plugin_gid_maps[2].count, 3);
+    }
+
+    #[test]
+    fn parse_plugin_gid_map_invalid() {
+        let mut config = Config::default();
+        set_argument(&mut config, "plugin-gid-map", Some("")).expect_err("parse should fail");
+        set_argument(&mut config, "plugin-gid-map", Some("1:2:3:4"))
+            .expect_err("parse should fail because too many arguments");
+        set_argument(&mut config, "plugin-gid-map", Some("blah:2:3"))
+            .expect_err("parse should fail because inner is not a number");
+        set_argument(&mut config, "plugin-gid-map", Some("1:blah:3"))
+            .expect_err("parse should fail because outer is not a number");
+        set_argument(&mut config, "plugin-gid-map", Some("1:2:blah"))
+            .expect_err("parse should fail because count is not a number");
+    }
 }