Merge changes I1ec20b6f,Id556f5ae

* changes:
  authfs: support resizing file
  authfs: Drop support of direct I/O
diff --git a/authfs/Android.bp b/authfs/Android.bp
index 85f2abb..8dabbd9 100644
--- a/authfs/Android.bp
+++ b/authfs/Android.bp
@@ -11,11 +11,13 @@
     edition: "2018",
     rustlibs: [
         "authfs_aidl_interface-rust",
+        "libandroid_logger",
         "libanyhow",
         "libauthfs_crypto_bindgen",
         "libcfg_if",
         "libfuse_rust",
         "liblibc",
+        "liblog_rust",
         "libstructopt",
         "libthiserror",
     ],
diff --git a/authfs/aidl/com/android/virt/fs/IVirtFdService.aidl b/authfs/aidl/com/android/virt/fs/IVirtFdService.aidl
index 189f43a..d3c0979 100644
--- a/authfs/aidl/com/android/virt/fs/IVirtFdService.aidl
+++ b/authfs/aidl/com/android/virt/fs/IVirtFdService.aidl
@@ -51,4 +51,7 @@
      * written.
      */
     int writeFile(int id, in byte[] buf, long offset);
+
+    /** Resizes the file backed by the given file ID to the new size. */
+    void resize(int id, long size);
 }
diff --git a/authfs/fd_server/src/main.rs b/authfs/fd_server/src/main.rs
index 4e5c5d3..204d1b1 100644
--- a/authfs/fd_server/src/main.rs
+++ b/authfs/fd_server/src/main.rs
@@ -206,6 +206,24 @@
             }
         }
     }
+
+    fn resize(&self, id: i32, size: i64) -> BinderResult<()> {
+        match &self.get_file_config(id)? {
+            FdConfig::Readonly { .. } => Err(StatusCode::INVALID_OPERATION.into()),
+            FdConfig::ReadWrite(file) => {
+                if size < 0 {
+                    return Err(new_binder_exception(
+                        ExceptionCode::ILLEGAL_ARGUMENT,
+                        "Invalid size to resize to",
+                    ));
+                }
+                file.set_len(size as u64).map_err(|e| {
+                    error!("resize: set_len error: {}", e);
+                    Status::from(ERROR_IO)
+                })
+            }
+        }
+    }
 }
 
 fn read_into_buf(file: &File, max_size: usize, offset: u64) -> io::Result<Vec<u8>> {
diff --git a/authfs/src/file.rs b/authfs/src/file.rs
index 9ff8ea5..4b43786 100644
--- a/authfs/src/file.rs
+++ b/authfs/src/file.rs
@@ -49,4 +49,7 @@
         }
         Ok(())
     }
+
+    /// Resizes the file to the new size.
+    fn resize(&self, size: u64) -> io::Result<()>;
 }
diff --git a/authfs/src/file/remote_file.rs b/authfs/src/file/remote_file.rs
index 9d614f5..bd99893 100644
--- a/authfs/src/file/remote_file.rs
+++ b/authfs/src/file/remote_file.rs
@@ -118,6 +118,17 @@
             .map_err(|e| io::Error::new(io::ErrorKind::Other, e.get_description()))?;
         Ok(size as usize) // within range because size is supposed to <= buf.len(), which is a usize
     }
+
+    fn resize(&self, size: u64) -> io::Result<()> {
+        let size =
+            i64::try_from(size).map_err(|_| io::Error::from_raw_os_error(libc::EOVERFLOW))?;
+        self.service
+            .lock()
+            .unwrap()
+            .resize(self.file_fd, size)
+            .map_err(|e| io::Error::new(io::ErrorKind::Other, e.get_description()))?;
+        Ok(())
+    }
 }
 
 impl ReadByChunk for RemoteFileEditor {
diff --git a/authfs/src/fsverity/builder.rs b/authfs/src/fsverity/builder.rs
index dd1bd04..1842425 100644
--- a/authfs/src/fsverity/builder.rs
+++ b/authfs/src/fsverity/builder.rs
@@ -15,7 +15,7 @@
  */
 
 use super::common::{build_fsverity_digest, merkle_tree_height, FsverityError};
-use crate::common::CHUNK_SIZE;
+use crate::common::{divide_roundup, CHUNK_SIZE};
 use crate::crypto::{CryptoError, Sha256Hash, Sha256Hasher};
 
 const HASH_SIZE: usize = Sha256Hasher::HASH_SIZE;
@@ -55,6 +55,19 @@
         self.file_size
     }
 
+    /// Grows the `MerkleLeaves` to the new file size. To keep the consistency, if any new leaves
+    /// are added, the leaves/hashes are as if the extended content is all zero.
+    ///
+    /// However, when the change shrinks the leaf number, `MerkleLeaves` does not know if the hash
+    /// of the last chunk has changed, or what the new value should be. As the result, it is up to
+    /// the caller to fix the last leaf if needed.
+    pub fn resize(&mut self, new_file_size: usize) {
+        let new_file_size = new_file_size as u64;
+        let leaves_size = divide_roundup(new_file_size, CHUNK_SIZE);
+        self.leaves.resize(leaves_size as usize, Sha256Hasher::HASH_OF_4096_ZEROS);
+        self.file_size = new_file_size;
+    }
+
     /// Updates the hash of the `index`-th leaf, and increase the size to `size_at_least` if the
     /// current size is smaller.
     pub fn update_hash(&mut self, index: usize, hash: &Sha256Hash, size_at_least: u64) {
@@ -196,6 +209,41 @@
         Ok(())
     }
 
+    #[test]
+    fn merkle_tree_grow_leaves() -> Result<()> {
+        let mut tree = MerkleLeaves::new();
+        tree.update_hash(0, &[42; HASH_SIZE], CHUNK_SIZE); // fake hash of a CHUNK_SIZE block
+
+        // Grows the leaves
+        tree.resize(4096 * 3 - 100);
+
+        assert!(tree.is_index_valid(0));
+        assert!(tree.is_index_valid(1));
+        assert!(tree.is_index_valid(2));
+        assert!(!tree.is_index_valid(3));
+        assert!(tree.is_consistent(1, &Sha256Hasher::HASH_OF_4096_ZEROS));
+        assert!(tree.is_consistent(2, &Sha256Hasher::HASH_OF_4096_ZEROS));
+        Ok(())
+    }
+
+    #[test]
+    fn merkle_tree_shrink_leaves() -> Result<()> {
+        let mut tree = MerkleLeaves::new();
+        tree.update_hash(0, &[42; HASH_SIZE], CHUNK_SIZE);
+        tree.update_hash(0, &[42; HASH_SIZE], CHUNK_SIZE * 3);
+
+        // Shrink the leaves
+        tree.resize(CHUNK_SIZE as usize * 2 - 100);
+
+        assert!(tree.is_index_valid(0));
+        assert!(tree.is_index_valid(1));
+        assert!(!tree.is_index_valid(2));
+        // The second chunk is a hole and full of zero. When shrunk, with zero padding, the hash
+        // happens to be consistent to a full-zero chunk.
+        assert!(tree.is_consistent(1, &Sha256Hasher::HASH_OF_4096_ZEROS));
+        Ok(())
+    }
+
     fn generate_fsverity_digest_sequentially(test_data: &[u8]) -> Result<Sha256Hash> {
         let mut tree = MerkleLeaves::new();
         for (index, chunk) in test_data.chunks(CHUNK_SIZE as usize).enumerate() {
diff --git a/authfs/src/fsverity/editor.rs b/authfs/src/fsverity/editor.rs
index 81ccd53..8468cc9 100644
--- a/authfs/src/fsverity/editor.rs
+++ b/authfs/src/fsverity/editor.rs
@@ -68,6 +68,13 @@
     }
 }
 
+fn debug_assert_usize_is_u64() {
+    // Since we don't need to support 32-bit CPU, make an assert to make conversion between
+    // u64 and usize easy below. Otherwise, we need to check `divide_roundup(offset + buf.len()
+    // <= usize::MAX` or handle `TryInto` errors.
+    debug_assert!(usize::MAX as u64 == u64::MAX, "Only 64-bit arch is supported");
+}
+
 /// VerifiedFileEditor provides an integrity layer to an underlying read-writable file, which may
 /// not be stored in a trusted environment. Only new, empty files are currently supported.
 pub struct VerifiedFileEditor<F: ReadByChunk + RandomWrite> {
@@ -150,10 +157,7 @@
 
 impl<F: ReadByChunk + RandomWrite> RandomWrite for VerifiedFileEditor<F> {
     fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
-        // Since we don't need to support 32-bit CPU, make an assert to make conversion between
-        // u64 and usize easy below. Otherwise, we need to check `divide_roundup(offset + buf.len()
-        // <= usize::MAX` or handle `TryInto` errors.
-        debug_assert!(usize::MAX as u64 == u64::MAX, "Only 64-bit arch is supported");
+        debug_assert_usize_is_u64();
 
         // The write range may not be well-aligned with the chunk boundary. There are various cases
         // to deal with:
@@ -212,6 +216,42 @@
         }
         Ok(buf.len())
     }
+
+    fn resize(&self, size: u64) -> io::Result<()> {
+        debug_assert_usize_is_u64();
+
+        let mut merkle_tree = self.merkle_tree.write().unwrap();
+        // In case when we are truncating the file, we may need to recalculate the hash of the (new)
+        // last chunk. Since the content is provided by the untrusted backend, we need to read the
+        // data back first, verify it, then override the truncated portion with 0-padding for
+        // hashing. As an optimization, we only need to read the data back if the new size isn't a
+        // multiple of CHUNK_SIZE (since the hash is already correct).
+        //
+        // The same thing does not need to happen when the size is growing. Since the new extended
+        // data is always 0, we can just resize the `MerkleLeaves`, where a new hash is always
+        // calculated from 4096 zeros.
+        if size < merkle_tree.file_size() && size % CHUNK_SIZE > 0 {
+            let new_tail_size = (size % CHUNK_SIZE) as usize;
+            let chunk_index = size / CHUNK_SIZE;
+            if new_tail_size > 0 {
+                let mut buf: ChunkBuffer = [0; CHUNK_SIZE as usize];
+                let s = self.read_chunk(chunk_index, &mut buf)?;
+                debug_assert!(new_tail_size <= s);
+
+                let zeros = vec![0; CHUNK_SIZE as usize - new_tail_size];
+                let new_hash = Sha256Hasher::new()?
+                    .update(&buf[..new_tail_size])?
+                    .update(&zeros)?
+                    .finalize()?;
+                merkle_tree.update_hash(chunk_index as usize, &new_hash, size);
+            }
+        }
+
+        self.file.resize(size)?;
+        merkle_tree.resize(size as usize);
+
+        Ok(())
+    }
 }
 
 impl<F: ReadByChunk + RandomWrite> ReadByChunk for VerifiedFileEditor<F> {
@@ -253,6 +293,13 @@
             self.data.borrow_mut().as_mut_slice()[begin..end].copy_from_slice(&buf);
             Ok(buf.len())
         }
+
+        fn resize(&self, size: u64) -> io::Result<()> {
+            let size: usize =
+                size.try_into().map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
+            self.data.borrow_mut().resize(size, 0);
+            Ok(())
+        }
     }
 
     impl ReadByChunk for InMemoryEditor {
@@ -470,6 +517,88 @@
         Ok(())
     }
 
+    #[test]
+    fn test_resize_to_same_size() -> Result<()> {
+        let file = VerifiedFileEditor::new(InMemoryEditor::new());
+        assert_eq!(file.write_at(&[1; 2048], 0)?, 2048);
+
+        assert!(file.resize(2048).is_ok());
+        assert_eq!(file.size(), 2048);
+
+        assert_eq!(
+            file.calculate_fsverity_digest()?,
+            to_u8_vec("fef1b4f19bb7a2cd944d7cdee44d1accb12726389ca5b0f61ac0f548ae40876f")
+                .as_slice()
+        );
+        Ok(())
+    }
+
+    #[test]
+    fn test_resize_to_grow() -> Result<()> {
+        let file = VerifiedFileEditor::new(InMemoryEditor::new());
+        assert_eq!(file.write_at(&[1; 2048], 0)?, 2048);
+
+        // Resize should grow with 0s.
+        assert!(file.resize(4096).is_ok());
+        assert_eq!(file.size(), 4096);
+
+        assert_eq!(
+            file.calculate_fsverity_digest()?,
+            to_u8_vec("9e0e2745c21e4e74065240936d2047340d96a466680c3c9d177b82433e7a0bb1")
+                .as_slice()
+        );
+        Ok(())
+    }
+
+    #[test]
+    fn test_resize_to_shrink() -> Result<()> {
+        let file = VerifiedFileEditor::new(InMemoryEditor::new());
+        assert_eq!(file.write_at(&[1; 4096], 0)?, 4096);
+
+        // Truncate.
+        file.resize(2048)?;
+        assert_eq!(file.size(), 2048);
+
+        assert_eq!(
+            file.calculate_fsverity_digest()?,
+            to_u8_vec("fef1b4f19bb7a2cd944d7cdee44d1accb12726389ca5b0f61ac0f548ae40876f")
+                .as_slice()
+        );
+        Ok(())
+    }
+
+    #[test]
+    fn test_resize_to_shrink_with_read_failure() -> Result<()> {
+        let mut writer = InMemoryEditor::new();
+        writer.fail_read = true;
+        let file = VerifiedFileEditor::new(writer);
+        assert_eq!(file.write_at(&[1; 4096], 0)?, 4096);
+
+        // A truncate needs a read back. If the read fail, the resize should fail.
+        assert!(file.resize(2048).is_err());
+        Ok(())
+    }
+
+    #[test]
+    fn test_resize_to_shirink_to_chunk_boundary() -> Result<()> {
+        let mut writer = InMemoryEditor::new();
+        writer.fail_read = true;
+        let file = VerifiedFileEditor::new(writer);
+        assert_eq!(file.write_at(&[1; 8192], 0)?, 8192);
+
+        // Truncate to a chunk boundary. A read error doesn't matter since we won't need to
+        // recalcuate the leaf hash.
+        file.resize(4096)?;
+        assert_eq!(file.size(), 4096);
+
+        assert_eq!(
+            file.calculate_fsverity_digest()?,
+            to_u8_vec("cd0875ca59c7d37e962c5e8f5acd3770750ac80225e2df652ce5672fd34500af")
+                .as_slice()
+        );
+        Ok(())
+    }
+
     fn to_u8_vec(hex_str: &str) -> Vec<u8> {
         assert!(hex_str.len() % 2 == 0);
         (0..hex_str.len())
diff --git a/authfs/src/fusefs.rs b/authfs/src/fusefs.rs
index d97291c..d2948c7 100644
--- a/authfs/src/fusefs.rs
+++ b/authfs/src/fusefs.rs
@@ -15,6 +15,7 @@
  */
 
 use anyhow::Result;
+use log::{debug, warn};
 use std::collections::BTreeMap;
 use std::convert::TryFrom;
 use std::ffi::CStr;
@@ -27,8 +28,8 @@
 use std::time::Duration;
 
 use fuse::filesystem::{
-    Context, DirEntry, DirectoryIterator, Entry, FileSystem, FsOptions, ZeroCopyReader,
-    ZeroCopyWriter,
+    Context, DirEntry, DirectoryIterator, Entry, FileSystem, FsOptions, SetattrValid,
+    ZeroCopyReader, ZeroCopyWriter,
 };
 use fuse::mount::MountOption;
 
@@ -257,27 +258,19 @@
         // return None as the handle.
         match self.get_file_config(&inode)? {
             FileConfig::LocalVerifiedReadonlyFile { .. }
-            | FileConfig::RemoteVerifiedReadonlyFile { .. } => {
-                check_access_mode(flags, libc::O_RDONLY)?;
-                // Once verified, and only if verified, the file content can be cached. This is not
-                // really needed for a local file, but is the behavior of RemoteVerifiedReadonlyFile
-                // later.
-                Ok((None, fuse::sys::OpenOptions::KEEP_CACHE))
-            }
-            FileConfig::LocalUnverifiedReadonlyFile { .. }
+            | FileConfig::LocalUnverifiedReadonlyFile { .. }
+            | FileConfig::RemoteVerifiedReadonlyFile { .. }
             | FileConfig::RemoteUnverifiedReadonlyFile { .. } => {
                 check_access_mode(flags, libc::O_RDONLY)?;
-                // Do not cache the content. This type of file is supposed to be verified using
-                // dm-verity. The filesystem mount over dm-verity already is already cached, so use
-                // direct I/O here to avoid double cache.
-                Ok((None, fuse::sys::OpenOptions::DIRECT_IO))
             }
             FileConfig::RemoteVerifiedNewFile { .. } => {
                 // No need to check access modes since all the modes are allowed to the
                 // read-writable file.
-                Ok((None, fuse::sys::OpenOptions::KEEP_CACHE))
             }
         }
+        // Always cache the file content. There is currently no need to support direct I/O or avoid
+        // the cache buffer. Memory mapping is only possible with cache enabled.
+        Ok((None, fuse::sys::OpenOptions::KEEP_CACHE))
     }
 
     fn read<W: io::Write + ZeroCopyWriter>(
@@ -333,6 +326,54 @@
             _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
         }
     }
+
+    fn setattr(
+        &self,
+        _ctx: Context,
+        inode: Inode,
+        attr: libc::stat64,
+        _handle: Option<Handle>,
+        valid: SetattrValid,
+    ) -> io::Result<(libc::stat64, Duration)> {
+        match self.get_file_config(&inode)? {
+            FileConfig::RemoteVerifiedNewFile { editor } => {
+                // Initialize the default stat.
+                let mut new_attr = create_stat(inode, editor.size(), FileMode::ReadWrite)?;
+                // `valid` indicates what fields in `attr` are valid. Update to return correctly.
+                if valid.contains(SetattrValid::SIZE) {
+                    // st_size is i64, but the cast should be safe since kernel should not give a
+                    // negative size.
+                    debug_assert!(attr.st_size >= 0);
+                    new_attr.st_size = attr.st_size;
+                    editor.resize(attr.st_size as u64)?;
+                }
+
+                if valid.contains(SetattrValid::MODE) {
+                    warn!("Changing st_mode is not currently supported");
+                    return Err(io::Error::from_raw_os_error(libc::ENOSYS));
+                }
+                if valid.contains(SetattrValid::UID) {
+                    warn!("Changing st_uid is not currently supported");
+                    return Err(io::Error::from_raw_os_error(libc::ENOSYS));
+                }
+                if valid.contains(SetattrValid::GID) {
+                    warn!("Changing st_gid is not currently supported");
+                    return Err(io::Error::from_raw_os_error(libc::ENOSYS));
+                }
+                if valid.contains(SetattrValid::CTIME) {
+                    debug!("Ignoring ctime change as authfs does not maintain timestamp currently");
+                }
+                if valid.intersects(SetattrValid::ATIME | SetattrValid::ATIME_NOW) {
+                    debug!("Ignoring atime change as authfs does not maintain timestamp currently");
+                }
+                if valid.intersects(SetattrValid::MTIME | SetattrValid::MTIME_NOW) {
+                    debug!("Ignoring mtime change as authfs does not maintain timestamp currently");
+                }
+                Ok((new_attr, DEFAULT_METADATA_TIMEOUT))
+            }
+            _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
+        }
+    }
 }
 
 /// Mount and start the FUSE instance. This requires CAP_SYS_ADMIN.
diff --git a/authfs/src/main.rs b/authfs/src/main.rs
index 60f603f..b30195a 100644
--- a/authfs/src/main.rs
+++ b/authfs/src/main.rs
@@ -82,6 +82,10 @@
     /// Debug only. A read-only local file without integrity check. Can be multiple.
     #[structopt(long, parse(try_from_str = parse_local_ro_file_unverified_ro_option))]
     local_ro_file_unverified: Vec<OptionLocalRoFileUnverified>,
+
+    /// Enable debugging features.
+    #[structopt(long)]
+    debug: bool,
 }
 
 struct OptionRemoteRoFile {
@@ -294,6 +298,12 @@
 
 fn main() -> Result<()> {
     let args = Args::from_args();
+
+    let log_level = if args.debug { log::Level::Debug } else { log::Level::Info };
+    android_logger::init_once(
+        android_logger::Config::default().with_tag("authfs").with_min_level(log_level),
+    );
+
     let file_pool = prepare_file_pool(&args)?;
     fusefs::loop_forever(file_pool, &args.mount_point)?;
     bail!("Unexpected exit after the handler loop")