Merge "Allow zipfuse to mount with fscontext and context"
diff --git a/apkverity/Android.bp b/apkverity/Android.bp
new file mode 100644
index 0000000..3d0dab5
--- /dev/null
+++ b/apkverity/Android.bp
@@ -0,0 +1,42 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_defaults {
+    name: "apkdmverity.defaults",
+    crate_name: "apkdmverity",
+    srcs: ["src/main.rs"],
+    edition: "2018",
+    prefer_rlib: true,
+    rustlibs: [
+        "libanyhow",
+        "libbitflags",
+        "libclap",
+        "liblibc",
+        "libnix",
+        "libnum_traits",
+        "libscopeguard",
+        "libuuid",
+    ],
+    proc_macros: ["libnum_derive"],
+    multilib: {
+        lib32: {
+            enabled: false,
+        },
+    },
+}
+
+rust_binary {
+    name: "apkdmverity",
+    defaults: ["apkdmverity.defaults"],
+}
+
+rust_test {
+    name: "apkdmverity.test",
+    defaults: ["apkdmverity.defaults"],
+    test_suites: ["general-tests"],
+    compile_multilib: "first",
+    rustlibs: [
+        "libtempfile",
+    ],
+}
diff --git a/apkverity/src/apksigv4.rs b/apkverity/src/apksigv4.rs
index f1ee0a4..7d8f318 100644
--- a/apkverity/src/apksigv4.rs
+++ b/apkverity/src/apksigv4.rs
@@ -57,7 +57,7 @@
 
 impl Version {
     fn from(val: u32) -> Result<Version> {
-        Self::from_u32(val).ok_or(anyhow!("{} is an unsupported version", val))
+        Self::from_u32(val).ok_or_else(|| anyhow!("{} is an unsupported version", val))
     }
 }
 
@@ -69,7 +69,7 @@
 
 impl HashAlgorithm {
     fn from(val: u32) -> Result<HashAlgorithm> {
-        Self::from_u32(val).ok_or(anyhow!("{} is an unsupported hash algorithm", val))
+        Self::from_u32(val).ok_or_else(|| anyhow!("{} is an unsupported hash algorithm", val))
     }
 }
 
@@ -157,7 +157,7 @@
     use std::io::Cursor;
 
     fn hexstring_from(s: &[u8]) -> String {
-        s.iter().map(|byte| format!("{:02x}", byte)).reduce(|i, j| i + &j).unwrap_or(String::new())
+        s.iter().map(|byte| format!("{:02x}", byte)).reduce(|i, j| i + &j).unwrap_or_default()
     }
 
     #[test]
diff --git a/apkverity/src/dm.rs b/apkverity/src/dm.rs
index ec4248c..8828b0a 100644
--- a/apkverity/src/dm.rs
+++ b/apkverity/src/dm.rs
@@ -41,9 +41,10 @@
 pub use verity::*;
 
 nix::ioctl_readwrite!(_dm_dev_create, DM_IOCTL, Cmd::DM_DEV_CREATE, DmIoctl);
-nix::ioctl_readwrite!(_dm_dev_remove, DM_IOCTL, Cmd::DM_DEV_REMOVE, DmIoctl);
 nix::ioctl_readwrite!(_dm_dev_suspend, DM_IOCTL, Cmd::DM_DEV_SUSPEND, DmIoctl);
 nix::ioctl_readwrite!(_dm_table_load, DM_IOCTL, Cmd::DM_TABLE_LOAD, DmIoctl);
+#[cfg(test)]
+nix::ioctl_readwrite!(_dm_dev_remove, DM_IOCTL, Cmd::DM_DEV_REMOVE, DmIoctl);
 
 fn dm_dev_create(dm: &DeviceMapper, ioctl: *mut DmIoctl) -> Result<i32> {
     // SAFETY: `ioctl` is copied into the kernel. It modifies the state in the kernel, not the
@@ -51,12 +52,6 @@
     Ok(unsafe { _dm_dev_create(dm.0.as_raw_fd(), ioctl) }?)
 }
 
-fn dm_dev_remove(dm: &DeviceMapper, ioctl: *mut DmIoctl) -> Result<i32> {
-    // SAFETY: `ioctl` is copied into the kernel. It modifies the state in the kernel, not the
-    // state of this process in any way.
-    Ok(unsafe { _dm_dev_remove(dm.0.as_raw_fd(), ioctl) }?)
-}
-
 fn dm_dev_suspend(dm: &DeviceMapper, ioctl: *mut DmIoctl) -> Result<i32> {
     // SAFETY: `ioctl` is copied into the kernel. It modifies the state in the kernel, not the
     // state of this process in any way.
@@ -69,6 +64,13 @@
     Ok(unsafe { _dm_table_load(dm.0.as_raw_fd(), ioctl) }?)
 }
 
+#[cfg(test)]
+fn dm_dev_remove(dm: &DeviceMapper, ioctl: *mut DmIoctl) -> Result<i32> {
+    // SAFETY: `ioctl` is copied into the kernel. It modifies the state in the kernel, not the
+    // state of this process in any way.
+    Ok(unsafe { _dm_dev_remove(dm.0.as_raw_fd(), ioctl) }?)
+}
+
 // `DmTargetSpec` is the header of the data structure for a device-mapper target. When doing the
 // ioctl, one of more `DmTargetSpec` (and its body) are appened to the `DmIoctl` struct.
 #[repr(C)]
@@ -90,7 +92,7 @@
 
     fn as_u8_slice(&self) -> &[u8; size_of::<Self>()] {
         // SAFETY: lifetime of the output reference isn't changed.
-        unsafe { std::mem::transmute::<&Self, &[u8; size_of::<Self>()]>(&self) }
+        unsafe { &*(&self as *const &Self as *const [u8; size_of::<Self>()]) }
     }
 }
 
@@ -116,7 +118,7 @@
 
     fn as_u8_slice(&self) -> &[u8; size_of::<Self>()] {
         // SAFETY: lifetime of the output reference isn't changed.
-        unsafe { std::mem::transmute::<&Self, &[u8; size_of::<Self>()]>(&self) }
+        unsafe { &*(&self as *const &Self as *const [u8; size_of::<Self>()]) }
     }
 }
 
@@ -150,8 +152,8 @@
         data.flags |= Flag::DM_READONLY_FLAG;
 
         let mut payload = Vec::with_capacity(payload_size);
-        payload.extend_from_slice(&data.as_u8_slice()[..]);
-        payload.extend_from_slice(&target.as_u8_slice()[..]);
+        payload.extend_from_slice(data.as_u8_slice());
+        payload.extend_from_slice(target.as_u8_slice());
         dm_table_load(&self, payload.as_mut_ptr() as *mut DmIoctl)?;
 
         // Step 3: activate the device (note: the term 'suspend' might be misleading, but it
@@ -166,6 +168,7 @@
     }
 
     /// Removes a mapper device
+    #[cfg(test)]
     pub fn delete_device_deferred(&self, name: &str) -> Result<()> {
         let mut data = DmIoctl::new(&name)?;
         data.flags |= Flag::DM_DEFERRED_REMOVE;
diff --git a/apkverity/src/dm/verity.rs b/apkverity/src/dm/verity.rs
index cfc9504..950b26e 100644
--- a/apkverity/src/dm/verity.rs
+++ b/apkverity/src/dm/verity.rs
@@ -35,6 +35,7 @@
 }
 
 /// The hash algorithm to use. SHA256 and SHA512 are supported.
+#[allow(dead_code)]
 pub enum DmVerityHashAlgorithm {
     SHA256,
     SHA512,
diff --git a/apkverity/src/loopdevice.rs b/apkverity/src/loopdevice.rs
index bb0e767..519e3bd 100644
--- a/apkverity/src/loopdevice.rs
+++ b/apkverity/src/loopdevice.rs
@@ -36,6 +36,7 @@
 // These are old-style ioctls, thus *_bad.
 nix::ioctl_none_bad!(_loop_ctl_get_free, LOOP_CTL_GET_FREE);
 nix::ioctl_write_ptr_bad!(_loop_configure, LOOP_CONFIGURE, loop_config);
+#[cfg(test)]
 nix::ioctl_none_bad!(_loop_clr_fd, LOOP_CLR_FD);
 
 fn loop_ctl_get_free(ctrl_file: &File) -> Result<i32> {
@@ -50,6 +51,7 @@
     Ok(unsafe { _loop_configure(device_file.as_raw_fd(), config) }?)
 }
 
+#[cfg(test)]
 fn loop_clr_fd(device_file: &File) -> Result<i32> {
     // SAFETY: this ioctl disassociates the loop device with `device_file`, where the FD will
     // remain opened afterward. The association itself is kept for open FDs.
@@ -122,13 +124,14 @@
         .write(true)
         .open(&device_path)
         .context(format!("failed to open {:?}", &device_path))?;
-    loop_configure(&device_file, &mut config)
+    loop_configure(&device_file, &config)
         .context(format!("Failed to configure {:?}", &device_path))?;
 
     Ok(PathBuf::from(device_path))
 }
 
 /// Detaches backing file from the loop device `path`.
+#[cfg(test)]
 pub fn detach<P: AsRef<Path>>(path: P) -> Result<()> {
     let device_file = OpenOptions::new().read(true).write(true).open(&path)?;
     loop_clr_fd(&device_file)?;
diff --git a/apkverity/src/loopdevice/sys.rs b/apkverity/src/loopdevice/sys.rs
index 2d4977b..3f10f22 100644
--- a/apkverity/src/loopdevice/sys.rs
+++ b/apkverity/src/loopdevice/sys.rs
@@ -24,6 +24,7 @@
 
 pub const LOOP_CTL_GET_FREE: libc::c_ulong = 0x4C82;
 pub const LOOP_CONFIGURE: libc::c_ulong = 0x4C0A;
+#[cfg(test)]
 pub const LOOP_CLR_FD: libc::c_ulong = 0x4C01;
 
 #[repr(C)]
diff --git a/apkverity/src/main.rs b/apkverity/src/main.rs
index 6fe12a0..5094c50 100644
--- a/apkverity/src/main.rs
+++ b/apkverity/src/main.rs
@@ -57,12 +57,19 @@
                 )
                 .required(true),
         )
+        .arg(Arg::with_name("verbose").short("v").long("verbose").help("Shows verbose output"))
         .get_matches();
 
     let apk = matches.value_of("apk").unwrap();
     let idsig = matches.value_of("idsig").unwrap();
     let name = matches.value_of("name").unwrap();
-    enable_verity(apk, idsig, name)?;
+    let ret = enable_verity(apk, idsig, name)?;
+    if matches.is_present("verbose") {
+        println!(
+            "data_device: {:?}, hash_device: {:?}, mapper_device: {:?}",
+            ret.data_device, ret.hash_device, ret.mapper_device
+        );
+    }
     Ok(())
 }
 
diff --git a/apkverity/src/util.rs b/apkverity/src/util.rs
index 415e99b..d2bc799 100644
--- a/apkverity/src/util.rs
+++ b/apkverity/src/util.rs
@@ -16,7 +16,6 @@
 
 use anyhow::{bail, Result};
 use nix::sys::stat::FileStat;
-use std::fs;
 use std::fs::File;
 use std::os::unix::fs::FileTypeExt;
 use std::os::unix::io::AsRawFd;
@@ -40,7 +39,7 @@
 
 /// Returns hexadecimal reprentation of a given byte array.
 pub fn hexstring_from(s: &[u8]) -> String {
-    s.iter().map(|byte| format!("{:02x}", byte)).reduce(|i, j| i + &j).unwrap_or(String::new())
+    s.iter().map(|byte| format!("{:02x}", byte)).reduce(|i, j| i + &j).unwrap_or_default()
 }
 
 /// fstat that accepts a path rather than FD
diff --git a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
index 13ca434..b3c3e27 100644
--- a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
@@ -177,7 +177,9 @@
         assertThat(abiList.length, is(1));
 
         final String libPath = "/mnt/apk/lib/" + abiList[0] + "/MicrodroidTestNativeLib.so";
-        assertThat(executeCommandOnMicrodroid("shell ls " + libPath), is(libPath));
+        assertThat(
+                executeCommandOnMicrodroid("shell ls -Z " + libPath),
+                is("u:object_r:system_file:s0 " + libPath));
 
         assertThat(
                 executeCommandOnMicrodroid(
diff --git a/zipfuse/src/main.rs b/zipfuse/src/main.rs
index 12c891c..9b70d08 100644
--- a/zipfuse/src/main.rs
+++ b/zipfuse/src/main.rs
@@ -39,35 +39,48 @@
 
 fn main() -> Result<()> {
     let matches = App::new("zipfuse")
+        .arg(
+            Arg::with_name("options")
+                .short("o")
+                .takes_value(true)
+                .required(false)
+                .help("Comma separated list of mount options")
+        )
         .arg(Arg::with_name("ZIPFILE").required(true))
         .arg(Arg::with_name("MOUNTPOINT").required(true))
         .get_matches();
 
     let zip_file = matches.value_of("ZIPFILE").unwrap().as_ref();
     let mount_point = matches.value_of("MOUNTPOINT").unwrap().as_ref();
-    run_fuse(zip_file, mount_point)?;
+    let options = matches.value_of("options");
+    run_fuse(zip_file, mount_point, options)?;
     Ok(())
 }
 
 /// Runs a fuse filesystem by mounting `zip_file` on `mount_point`.
-pub fn run_fuse(zip_file: &Path, mount_point: &Path) -> Result<()> {
+pub fn run_fuse(zip_file: &Path, mount_point: &Path, extra_options: Option<&str>) -> Result<()> {
     const MAX_READ: u32 = 1 << 20; // TODO(jiyong): tune this
     const MAX_WRITE: u32 = 1 << 13; // This is a read-only filesystem
 
     let dev_fuse = OpenOptions::new().read(true).write(true).open("/dev/fuse")?;
 
+    let mut mount_options = vec![
+        MountOption::FD(dev_fuse.as_raw_fd()),
+        MountOption::RootMode(libc::S_IFDIR | libc::S_IXUSR | libc::S_IXGRP | libc::S_IXOTH),
+        MountOption::AllowOther,
+        MountOption::UserId(0),
+        MountOption::GroupId(0),
+        MountOption::MaxRead(MAX_READ),
+    ];
+    if let Some(value) = extra_options {
+        mount_options.push(MountOption::Extra(value));
+    }
+
     fuse::mount(
         mount_point,
         "zipfuse",
         libc::MS_NOSUID | libc::MS_NODEV | libc::MS_RDONLY,
-        &[
-            MountOption::FD(dev_fuse.as_raw_fd()),
-            MountOption::RootMode(libc::S_IFDIR | libc::S_IXUSR | libc::S_IXGRP | libc::S_IXOTH),
-            MountOption::AllowOther,
-            MountOption::UserId(0),
-            MountOption::GroupId(0),
-            MountOption::MaxRead(MAX_READ),
-        ],
+        &mount_options,
     )?;
     Ok(fuse::worker::start_message_loop(dev_fuse, MAX_READ, MAX_WRITE, ZipFuse::new(zip_file)?)?)
 }
@@ -388,7 +401,7 @@
         let zip_path = PathBuf::from(zip_path);
         let mnt_path = PathBuf::from(mnt_path);
         std::thread::spawn(move || {
-            crate::run_fuse(&zip_path, &mnt_path).unwrap();
+            crate::run_fuse(&zip_path, &mnt_path, None).unwrap();
         });
     }
 
diff --git a/zipfuse/zipfuse.rc b/zipfuse/zipfuse.rc
index 97306ea..ccd94b6 100644
--- a/zipfuse/zipfuse.rc
+++ b/zipfuse/zipfuse.rc
@@ -1,2 +1,2 @@
-service zipfuse /system/bin/zipfuse /dev/block/by-name/microdroid-apk /mnt/apk
+service zipfuse /system/bin/zipfuse -o fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0 /dev/block/by-name/microdroid-apk /mnt/apk
     disabled