Add a uid_gid test for zipfuse

Also modified the test framework a bit to make customizing test a bit
easier.

Bug: 264668376
Test: atest ZipFuseTest
Change-Id: I71c2f6a9804d908cb52b1c2b5d69b9d3a0e64db6
diff --git a/zipfuse/src/main.rs b/zipfuse/src/main.rs
index ffa0a4a..365d236 100644
--- a/zipfuse/src/main.rs
+++ b/zipfuse/src/main.rs
@@ -477,30 +477,40 @@
     use std::fs;
     use std::fs::File;
     use std::io::Write;
+    use std::os::unix::fs::MetadataExt;
     use std::path::{Path, PathBuf};
     use std::time::{Duration, Instant};
     use zip::write::FileOptions;
 
+    #[derive(Default)]
+    struct Options {
+        noexec: bool,
+        uid: u32,
+        gid: u32,
+    }
+
     #[cfg(not(target_os = "android"))]
-    fn start_fuse(zip_path: &Path, mnt_path: &Path, noexec: bool) {
+    fn start_fuse(zip_path: &Path, mnt_path: &Path, opt: Options) {
         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, None, noexec, 0, 0).unwrap();
+            crate::run_fuse(&zip_path, &mnt_path, None, opt.noexec, opt.uid, opt.gid).unwrap();
         });
     }
 
     #[cfg(target_os = "android")]
-    fn start_fuse(zip_path: &Path, mnt_path: &Path, noexec: bool) {
+    fn start_fuse(zip_path: &Path, mnt_path: &Path, opt: Options) {
         // Note: for some unknown reason, running a thread to serve fuse doesn't work on Android.
         // Explicitly spawn a zipfuse process instead.
         // TODO(jiyong): fix this
-        let noexec = if noexec { "--noexec" } else { "" };
+        let noexec = if opt.noexec { "--noexec" } else { "" };
         assert!(std::process::Command::new("sh")
             .arg("-c")
             .arg(format!(
-                "/data/local/tmp/zipfuse {} {} {}",
+                "/data/local/tmp/zipfuse {} -u {} -g {} {} {}",
                 noexec,
+                opt.uid,
+                opt.gid,
                 zip_path.display(),
                 mnt_path.display()
             ))
@@ -529,11 +539,11 @@
     // Creates a zip file, adds some files to the zip file, mounts it using zipfuse, runs the check
     // routine, and finally unmounts.
     fn run_test(add: fn(&mut zip::ZipWriter<File>), check: fn(&std::path::Path)) {
-        run_test_noexec(false, add, check);
+        run_test_with_options(Default::default(), add, check);
     }
 
-    fn run_test_noexec(
-        noexec: bool,
+    fn run_test_with_options(
+        opt: Options,
         add: fn(&mut zip::ZipWriter<File>),
         check: fn(&std::path::Path),
     ) {
@@ -553,7 +563,7 @@
         let mnt_path = test_dir.path().join("mnt");
         assert!(fs::create_dir(&mnt_path).is_ok());
 
-        start_fuse(&zip_path, &mnt_path, noexec);
+        start_fuse(&zip_path, &mnt_path, opt);
 
         let mnt_path = test_dir.path().join("mnt");
         // Give some time for the fuse to boot up
@@ -650,14 +660,37 @@
         });
 
         // Mounting with noexec results in permissions denial when running an executable.
-        let noexec = true;
-        run_test_noexec(noexec, add_executable, |root| {
+        let opt = Options { noexec: true, ..Default::default() };
+        run_test_with_options(opt, add_executable, |root| {
             let res = std::process::Command::new(root.join("executable")).status();
             assert!(matches!(res.unwrap_err().kind(), std::io::ErrorKind::PermissionDenied));
         });
     }
 
     #[test]
+    fn uid_gid() {
+        const UID: u32 = 100;
+        const GID: u32 = 200;
+        run_test_with_options(
+            Options { noexec: true, uid: UID, gid: GID },
+            |zip| {
+                zip.start_file("foo", FileOptions::default()).unwrap();
+                zip.write_all(b"0123456789").unwrap();
+            },
+            |root| {
+                let path = root.join("foo");
+
+                let metadata = fs::metadata(&path);
+                assert!(metadata.is_ok());
+                let metadata = metadata.unwrap();
+
+                assert_eq!(UID, metadata.uid());
+                assert_eq!(GID, metadata.gid());
+            },
+        );
+    }
+
+    #[test]
     fn single_dir() {
         run_test(
             |zip| {
@@ -769,8 +802,8 @@
         let mnt_path = test_dir.join("mnt");
         assert!(fs::create_dir(&mnt_path).is_ok());
 
-        let noexec = false;
-        start_fuse(zip_path, &mnt_path, noexec);
+        let opt = Options { noexec: false, ..Default::default() };
+        start_fuse(zip_path, &mnt_path, opt);
 
         // Give some time for the fuse to boot up
         assert!(wait_for_mount(&mnt_path).is_ok());