Use fsverity metadata file for authfs

Instead of alternative signature and merkle tree, authfs will use
.fsv_meta files, which are installed to the system partition with
PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA Makefile.

Bug: 205987437
Test: atest ComposHostTestCases AuthFsTestCase
Test: run "atest ." inside authfs/tests
Change-Id: Ia9db78663e0e322c7a59305c67ac5b84716d8efe
diff --git a/authfs/src/fsverity/metadata/Android.bp b/authfs/src/fsverity/metadata/Android.bp
new file mode 100644
index 0000000..39ce515
--- /dev/null
+++ b/authfs/src/fsverity/metadata/Android.bp
@@ -0,0 +1,20 @@
+rust_bindgen {
+    name: "libauthfs_fsverity_metadata_bindgen",
+    wrapper_src: "metadata.hpp",
+    crate_name: "authfs_fsverity_metadata_bindgen",
+    source_stem: "metadata_bindings",
+    apex_available: ["com.android.virt"],
+}
+
+rust_library {
+    name: "libauthfs_fsverity_metadata",
+    crate_name: "authfs_fsverity_metadata",
+    srcs: [
+        "metadata.rs",
+    ],
+    rustlibs: [
+        "libauthfs_fsverity_metadata_bindgen",
+    ],
+    edition: "2018",
+    apex_available: ["com.android.virt"],
+}
diff --git a/authfs/src/fsverity/metadata/metadata.hpp b/authfs/src/fsverity/metadata/metadata.hpp
new file mode 100644
index 0000000..7bbd3da
--- /dev/null
+++ b/authfs/src/fsverity/metadata/metadata.hpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AUTHFS_FSVERITY_METADATA_H
+#define AUTHFS_FSVERITY_METADATA_H
+
+// This file contains the format of fs-verity metadata (.fsv_meta).
+//
+// The header format of .fsv_meta is:
+//
+// +-----------+---------------------------------------------+------------+
+// |  Address  |  Description                                |    Size    |
+// +-----------+---------------------------------------------+------------+
+// |  0x0000   |  32-bit LE, version of the format           |     4      |
+// |           |                                             |            |
+// |  0x0004   |  fsverity_descriptor (see linux/fsverity.h) |    256     |
+// |           |                                             |            |
+// |  0x0104   |  32-bit LE, type of signature               |     4      |
+// |           |  (0: NONE, 1: PKCS7, 2: RAW)                |            |
+// |           |                                             |            |
+// |  0x0108   |  32-bit LE, size of signature               |     4      |
+// |           |                                             |            |
+// |  0x010C   |  signature                                  | See 0x0108 |
+// +-----------+---------------------------------------------+------------+
+//
+// After the header, merkle tree dump exists at the first 4K boundary. Usually it's 0x1000, but it
+// could be, for example, 0x2000 or 0x3000, depending on the size of header.
+//
+// TODO(b/193113326): sync with build/make/tools/releasetools/fsverity_metadata_generator.py
+
+#include <stddef.h>
+#include <stdint.h>
+#include <linux/fsverity.h>
+
+const uint64_t CHUNK_SIZE = 4096;
+
+enum class FSVERITY_SIGNATURE_TYPE : __le32 {
+    NONE = 0,
+    PKCS7 = 1,
+    RAW = 2,
+};
+
+struct fsverity_metadata_header {
+    __le32 version;
+    fsverity_descriptor descriptor;
+    FSVERITY_SIGNATURE_TYPE signature_type;
+    __le32 signature_size;
+} __attribute__((packed));
+
+#endif   // AUTHFS_FSVERITY_METADATA_H
diff --git a/authfs/src/fsverity/metadata/metadata.rs b/authfs/src/fsverity/metadata/metadata.rs
new file mode 100644
index 0000000..0092bee
--- /dev/null
+++ b/authfs/src/fsverity/metadata/metadata.rs
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Rust bindgen interface for FSVerity Metadata file (.fsv_meta)
+use authfs_fsverity_metadata_bindgen::{
+    fsverity_metadata_header, FSVERITY_SIGNATURE_TYPE_NONE, FSVERITY_SIGNATURE_TYPE_PKCS7,
+    FSVERITY_SIGNATURE_TYPE_RAW,
+};
+
+use std::cmp::min;
+use std::os::unix::fs::MetadataExt;
+
+/// Structure for parsed metadata.
+pub struct FSVerityMetadata {
+    /// Header for the metadata.
+    pub header: fsverity_metadata_header,
+
+    /// Optional signature for the metadata.
+    pub signature: Option<Vec<u8>>,
+
+    metadata_file: File,
+
+    merkle_tree_offset: u64,
+}
+
+impl FSVerityMetadata {
+    /// Read the raw Merkle tree from the metadata, if it exists. The API semantics is similar to a
+    /// regular pread(2), and may not return full requested buffer.
+    pub fn read_merkle_tree(&self, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
+        let start = self.merkle_tree_offset + offset;
+        let end = min(self.metadata_file.metadata()?.size(), start + buf.len() as u64);
+        let read_size = (end - start) as usize;
+        debug_assert!(read_size <= buf.len());
+        self.metadata_file.read_exact_at(&mut buf[..read_size], start)?;
+        Ok(read_size)
+    }
+}
+
+use std::ffi::OsString;
+use std::fs::File;
+use std::io::{self, Read, Seek};
+use std::mem::{size_of, zeroed};
+use std::os::unix::fs::FileExt;
+use std::path::{Path, PathBuf};
+use std::slice::from_raw_parts_mut;
+
+/// Common block and page size in Linux.
+pub const CHUNK_SIZE: u64 = authfs_fsverity_metadata_bindgen::CHUNK_SIZE;
+
+/// Derive a path of metadata for a given path.
+/// e.g. "system/framework/foo.jar" -> "system/framework/foo.jar.fsv_meta"
+pub fn get_fsverity_metadata_path(path: &Path) -> PathBuf {
+    let mut os_string: OsString = path.into();
+    os_string.push(".fsv_meta");
+    os_string.into()
+}
+
+/// Parse metadata from given file, and returns a structure for the metadata.
+pub fn parse_fsverity_metadata(mut metadata_file: File) -> io::Result<Box<FSVerityMetadata>> {
+    let header_size = size_of::<fsverity_metadata_header>();
+
+    // SAFETY: the header doesn't include any pointers
+    let header: fsverity_metadata_header = unsafe {
+        let mut header: fsverity_metadata_header = zeroed();
+        let buffer = from_raw_parts_mut(
+            &mut header as *mut fsverity_metadata_header as *mut u8,
+            header_size,
+        );
+        metadata_file.read_exact(buffer)?;
+
+        // TODO(inseob): This doesn't seem ideal. Maybe we can consider nom?
+        header.version = u32::from_le(header.version);
+        header.descriptor.data_size = u64::from_le(header.descriptor.data_size);
+        header.signature_type = u32::from_le(header.signature_type);
+        header.signature_size = u32::from_le(header.signature_size);
+        header
+    };
+
+    if header.version != 1 {
+        return Err(io::Error::new(io::ErrorKind::Other, "unsupported metadata version"));
+    }
+
+    let signature = match header.signature_type {
+        FSVERITY_SIGNATURE_TYPE_NONE => None,
+        FSVERITY_SIGNATURE_TYPE_PKCS7 | FSVERITY_SIGNATURE_TYPE_RAW => {
+            // TODO: unpad pkcs7?
+            let mut buf = vec![0u8; header.signature_size as usize];
+            metadata_file.read_exact(&mut buf)?;
+            Some(buf)
+        }
+        _ => return Err(io::Error::new(io::ErrorKind::Other, "unknown signature type")),
+    };
+
+    // merkle tree is at the next 4K boundary
+    let merkle_tree_offset =
+        (metadata_file.stream_position()? + CHUNK_SIZE - 1) / CHUNK_SIZE * CHUNK_SIZE;
+
+    Ok(Box::new(FSVerityMetadata { header, signature, metadata_file, merkle_tree_offset }))
+}