Use fs-verity digests proto to build the directory

The proto contains path -> digest mapping. In this change, we start to
use the provided protobuf to build the directory. Digest is not yet
used.

Bug: 206869687
Test: atest AuthFsHostTest ComposHostTestCases MicrodroidTestCase
Change-Id: I0f03219c1243f20f9ec5099a451c6f588add9608
diff --git a/authfs/Android.bp b/authfs/Android.bp
index 471b0cf..ef78d4e 100644
--- a/authfs/Android.bp
+++ b/authfs/Android.bp
@@ -18,10 +18,12 @@
         "libbinder_rpc_unstable_bindgen",
         "libbinder_rs",
         "libcfg_if",
+        "libfsverity_digests_proto_rust",
         "libfuse_rust",
         "liblibc",
         "liblog_rust",
         "libnix",
+        "libprotobuf",
         "libstructopt",
         "libthiserror",
     ],
diff --git a/authfs/service/src/authfs.rs b/authfs/service/src/authfs.rs
index e1d820a..c941360 100644
--- a/authfs/service/src/authfs.rs
+++ b/authfs/service/src/authfs.rs
@@ -152,7 +152,6 @@
     }
     for conf in in_dir_fds {
         args.push(OsString::from("--remote-ro-dir"));
-        // TODO(206869687): Replace /dev/null with the real path when possible.
         args.push(OsString::from(format!("{}:{}:{}", conf.fd, conf.manifestPath, conf.prefix)));
     }
     for conf in out_dir_fds {
diff --git a/authfs/src/main.rs b/authfs/src/main.rs
index 18b7b51..0fa3db7 100644
--- a/authfs/src/main.rs
+++ b/authfs/src/main.rs
@@ -27,9 +27,11 @@
 //! of the actual file name, the exposed file names through AuthFS are currently integer, e.g.
 //! /mountpoint/42.
 
-use anyhow::{bail, Result};
+use anyhow::{anyhow, bail, Result};
 use log::error;
+use protobuf::Message;
 use std::convert::TryInto;
+use std::fs::File;
 use std::path::{Path, PathBuf};
 use structopt::StructOpt;
 
@@ -47,6 +49,7 @@
 };
 use fsstat::RemoteFsStatsReader;
 use fsverity::{VerifiedFileEditor, VerifiedFileReader};
+use fsverity_digests_proto::fsverity_digests::FSVerityDigests;
 use fusefs::{AuthFs, AuthFsEntry};
 
 #[derive(StructOpt)]
@@ -93,10 +96,10 @@
     /// remote host may be included in the mapping file, so the directory view may be partial. The
     /// directory structure won't change throughout the filesystem lifetime.
     ///
-    /// For example, `--remote-ro-dir 5:/path/to/mapping:/prefix/` tells the filesystem to
+    /// For example, `--remote-ro-dir 5:/path/to/mapping:prefix/` tells the filesystem to
     /// construct a directory structure defined in the mapping file at $MOUNTPOINT/5, which may
-    /// include a file like /5/system/framework/framework.jar. "/prefix/" tells the filesystem to
-    /// strip the path (e.g. "/system/") from the mount point to match the expected location of the
+    /// include a file like /5/system/framework/framework.jar. "prefix/" tells the filesystem to
+    /// strip the path (e.g. "system/") from the mount point to match the expected location of the
     /// remote FD (e.g. a directory FD of "/system" in the remote).
     #[structopt(long, parse(try_from_str = parse_remote_new_ro_dir_option))]
     remote_ro_dir: Vec<OptionRemoteRoDir>,
@@ -131,11 +134,9 @@
     /// A mapping file that describes the expecting file/directory structure and integrity metadata
     /// in the remote directory. The file contains serialized protobuf of
     /// android.security.fsverity.FSVerityDigests.
-    /// TODO(206869687): Really use the file when it's generated.
-    #[allow(dead_code)]
     mapping_file_path: PathBuf,
 
-    prefix: PathBuf,
+    prefix: String,
 }
 
 fn parse_remote_ro_file_option(option: &str) -> Result<OptionRemoteRoFile> {
@@ -157,7 +158,7 @@
     Ok(OptionRemoteRoDir {
         remote_dir_fd: strs[0].parse::<i32>().unwrap(),
         mapping_file_path: PathBuf::from(strs[1]),
-        prefix: PathBuf::from(strs[2]),
+        prefix: String::from(strs[2]),
     })
 }
 
@@ -260,42 +261,25 @@
             AuthFsEntry::ReadonlyDirectory { dir: InMemoryDir::new() },
         )?;
 
-        // TODO(206869687): Read actual path from config.mapping_file_path when it's generated.
-        let paths = vec![
-            Path::new("/system/framework/com.android.location.provider.jar"),
-            Path::new("/system/framework/ethernet-service.jar"),
-            Path::new("/system/framework/ext.jar"),
-            Path::new("/system/framework/framework-graphics.jar"),
-            Path::new("/system/framework/framework.jar"),
-            Path::new("/system/framework/ims-common.jar"),
-            Path::new("/system/framework/services.jar"),
-            Path::new("/system/framework/services.jar.prof"),
-            Path::new("/system/framework/telephony-common.jar"),
-            Path::new("/system/framework/voip-common.jar"),
-            Path::new("/system/etc/boot-image.prof"),
-            Path::new("/system/etc/classpaths/bootclasspath.pb"),
-            Path::new("/system/etc/classpaths/systemserverclasspath.pb"),
-            Path::new("/system/etc/dirty-image-objects"),
-        ];
-
-        for path in &paths {
+        // Build the directory tree based on the mapping file.
+        let mut reader = File::open(&config.mapping_file_path)?;
+        let proto = FSVerityDigests::parse_from_reader(&mut reader)?;
+        for path_str in proto.digests.keys() {
             let file_entry = {
+                let remote_path_str = path_str.strip_prefix(&config.prefix).ok_or_else(|| {
+                    anyhow!("Expect path {} to match prefix {}", path_str, config.prefix)
+                })?;
                 // TODO(205883847): Not all files will be used. Open the remote file lazily.
-                let related_path = path.strip_prefix(&config.prefix)?;
                 let remote_file = RemoteFileReader::new_by_path(
                     service.clone(),
                     config.remote_dir_fd,
-                    related_path,
+                    Path::new(remote_path_str),
                 )?;
                 let file_size = service.getFileSize(remote_file.get_remote_fd())?.try_into()?;
                 // TODO(206869687): Switch to VerifiedReadonly
                 AuthFsEntry::UnverifiedReadonly { reader: remote_file, file_size }
             };
-            authfs.add_entry_at_ro_dir_by_path(
-                dir_root_inode,
-                path.strip_prefix("/")?,
-                file_entry,
-            )?;
+            authfs.add_entry_at_ro_dir_by_path(dir_root_inode, Path::new(path_str), file_entry)?;
         }
     }
 
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
index 819061b..ef544b2 100644
--- a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
+++ b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
@@ -70,6 +70,14 @@
     /** Path to authfs on Microdroid */
     private static final String AUTHFS_BIN = "/system/bin/authfs";
 
+    /** Idsig paths to be created for each APK in the "extra_apks" of vm_config.json. */
+    private static final String[] EXTRA_IDSIG_PATHS = new String[] {
+        TEST_DIR + "BuildManifest.apk.idsig",
+    };
+
+    /** Build manifest path in the VM. 0 is the index of extra_apks in vm_config.json. */
+    private static final String BUILD_MANIFEST_PATH = "/mnt/extra-apk/0/assets/build_manifest.pb";
+
     /** Plenty of time for authfs to get ready */
     private static final int AUTHFS_INIT_TIMEOUT_MS = 3000;
 
@@ -111,13 +119,14 @@
         CLog.i("Starting the shared VM");
         final String apkName = "MicrodroidTestApp.apk";
         final String packageName = "com.android.microdroid.test";
-        final String configPath = "assets/vm_config.json"; // path inside the APK
+        final String configPath = "assets/vm_config_extra_apk.json"; // path inside the APK
         sCid =
                 startMicrodroid(
                         androidDevice,
                         testInfo.getBuildInfo(),
                         apkName,
                         packageName,
+                        EXTRA_IDSIG_PATHS,
                         configPath,
                         /* debug */ true,
                         /* use default memoryMib */ 0,
@@ -491,9 +500,8 @@
         // Setup
         String authfsInputDir = MOUNT_DIR + "/3";
         runFdServerOnAndroid("--open-dir 3:/system", "--ro-dirs 3");
-        // TODO(206869687): Replace /dev/null with real manifest file when it's generated. We
-        // currently hard-coded the files for the test manually, and ignore the integrity check.
-        runAuthFsOnMicrodroid("--remote-ro-dir 3:/dev/null:/system --cid " + VMADDR_CID_HOST);
+        runAuthFsOnMicrodroid("--remote-ro-dir 3:" + BUILD_MANIFEST_PATH + ":system/ --cid "
+                + VMADDR_CID_HOST);
 
         // Action
         String actualHash =
@@ -509,9 +517,8 @@
         // Setup
         String authfsInputDir = MOUNT_DIR + "/3";
         runFdServerOnAndroid("--open-dir 3:/system", "--ro-dirs 3");
-        // TODO(206869687): Replace /dev/null with real manifest file when it's generated. We
-        // currently hard-coded the files for the test manually, and ignore the integrity check.
-        runAuthFsOnMicrodroid("--remote-ro-dir 3:/dev/null:/system --cid " + VMADDR_CID_HOST);
+        runAuthFsOnMicrodroid("--remote-ro-dir 3:" + BUILD_MANIFEST_PATH + ":system/ --cid "
+                + VMADDR_CID_HOST);
 
         // Verify
         runOnMicrodroid("test -f " + authfsInputDir + "/system/framework/services.jar");