authfs: instantiate read-only files lazily

The earlier implementation instantiates all remote files (defined in the
build manifest) at start. The instantiation includes 1) asking fd_server
to create the actual FDs, and 2) fetch the file's Merkle tree. The
instantiation of all files happens during AuthFS start, even if many
files aren't/won't be used.

This change makes the instnaitation lazy, i.e. 1) and 2) will only
happen when the file is first used.

Bug: 205883847
Test: atest AuthFsHostTest ComposHostTestCases
Change-Id: I2e4ee48b5442b56937e212505526b2f26eaadd91
diff --git a/authfs/src/fusefs/file.rs b/authfs/src/fusefs/file.rs
new file mode 100644
index 0000000..8c02281
--- /dev/null
+++ b/authfs/src/fusefs/file.rs
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+use log::error;
+use std::convert::TryInto;
+use std::io;
+use std::path::PathBuf;
+use std::sync::Mutex;
+
+use crate::file::{
+    ChunkBuffer, EagerChunkReader, ReadByChunk, RemoteFileReader, RemoteMerkleTreeReader,
+    VirtFdService,
+};
+use crate::fsverity::{merkle_tree_size, VerifiedFileReader};
+
+enum FileInfo {
+    ByPathUnderDirFd(i32, PathBuf),
+    ByFd(i32),
+}
+
+type Reader = VerifiedFileReader<RemoteFileReader, EagerChunkReader>;
+
+/// A lazily created read-only file that is verified against the given fs-verity digest.
+///
+/// The main purpose of this struct is to wrap and construct `VerifiedFileReader` lazily.
+pub struct LazyVerifiedReadonlyFile {
+    expected_digest: Vec<u8>,
+
+    service: VirtFdService,
+    file_info: FileInfo,
+
+    /// A lazily instantiated reader.
+    reader: Mutex<Option<Reader>>,
+}
+
+impl LazyVerifiedReadonlyFile {
+    /// Prepare the file by a remote path, related to a remote directory FD.
+    pub fn prepare_by_path(
+        service: VirtFdService,
+        remote_dir_fd: i32,
+        remote_path: PathBuf,
+        expected_digest: Vec<u8>,
+    ) -> Self {
+        LazyVerifiedReadonlyFile {
+            service,
+            file_info: FileInfo::ByPathUnderDirFd(remote_dir_fd, remote_path),
+            expected_digest,
+            reader: Mutex::new(None),
+        }
+    }
+
+    /// Prepare the file by a remote file FD.
+    pub fn prepare_by_fd(service: VirtFdService, remote_fd: i32, expected_digest: Vec<u8>) -> Self {
+        LazyVerifiedReadonlyFile {
+            service,
+            file_info: FileInfo::ByFd(remote_fd),
+            expected_digest,
+            reader: Mutex::new(None),
+        }
+    }
+
+    fn ensure_init_then<F, T>(&self, callback: F) -> io::Result<T>
+    where
+        F: FnOnce(&Reader) -> io::Result<T>,
+    {
+        let mut reader = self.reader.lock().unwrap();
+        if reader.is_none() {
+            let remote_file = match &self.file_info {
+                FileInfo::ByPathUnderDirFd(dir_fd, related_path) => {
+                    RemoteFileReader::new_by_path(self.service.clone(), *dir_fd, related_path)?
+                }
+                FileInfo::ByFd(file_fd) => RemoteFileReader::new(self.service.clone(), *file_fd),
+            };
+            let remote_fd = remote_file.get_remote_fd();
+            let file_size = self
+                .service
+                .getFileSize(remote_fd)
+                .map_err(|e| {
+                    error!("Failed to get file size of remote fd {}: {}", remote_fd, e);
+                    io::Error::from_raw_os_error(libc::EIO)
+                })?
+                .try_into()
+                .map_err(|e| {
+                    error!("Failed convert file size: {}", e);
+                    io::Error::from_raw_os_error(libc::EIO)
+                })?;
+            let instance = VerifiedFileReader::new(
+                remote_file,
+                file_size,
+                &self.expected_digest,
+                EagerChunkReader::new(
+                    RemoteMerkleTreeReader::new(self.service.clone(), remote_fd),
+                    merkle_tree_size(file_size),
+                )?,
+            )
+            .map_err(|e| {
+                error!("Failed instantiate a verified file reader: {}", e);
+                io::Error::from_raw_os_error(libc::EIO)
+            })?;
+            *reader = Some(instance);
+        }
+        callback(reader.as_ref().unwrap())
+    }
+
+    pub fn file_size(&self) -> io::Result<u64> {
+        self.ensure_init_then(|reader| Ok(reader.file_size))
+    }
+}
+
+impl ReadByChunk for LazyVerifiedReadonlyFile {
+    fn read_chunk(&self, chunk_index: u64, buf: &mut ChunkBuffer) -> io::Result<usize> {
+        self.ensure_init_then(|reader| reader.read_chunk(chunk_index, buf))
+    }
+}