authfs: Support write config/operation in fuse

Bug: 171279640
Test: atest
Test: tools/device-test.sh

Change-Id: Ic611f72d51a5522d9ec6e6fdc82c115b5782c4ac
diff --git a/authfs/src/fusefs.rs b/authfs/src/fusefs.rs
index bdf7cd8..13ec87d 100644
--- a/authfs/src/fusefs.rs
+++ b/authfs/src/fusefs.rs
@@ -26,12 +26,18 @@
 use std::path::Path;
 use std::time::Duration;
 
-use fuse::filesystem::{Context, DirEntry, DirectoryIterator, Entry, FileSystem, ZeroCopyWriter};
+use fuse::filesystem::{
+    Context, DirEntry, DirectoryIterator, Entry, FileSystem, FsOptions, ZeroCopyReader,
+    ZeroCopyWriter,
+};
 use fuse::mount::MountOption;
 
 use crate::common::{divide_roundup, ChunkedSizeIter, CHUNK_SIZE};
-use crate::file::{LocalFileReader, ReadOnlyDataByChunk, RemoteFileReader, RemoteMerkleTreeReader};
-use crate::fsverity::VerifiedFileReader;
+use crate::file::{
+    LocalFileReader, RandomWrite, ReadOnlyDataByChunk, RemoteFileEditor, RemoteFileReader,
+    RemoteMerkleTreeReader,
+};
+use crate::fsverity::{VerifiedFileEditor, VerifiedFileReader};
 
 const DEFAULT_METADATA_TIMEOUT: std::time::Duration = Duration::from_secs(5);
 
@@ -43,6 +49,7 @@
     LocalUnverifiedReadonlyFile(LocalFileReader, u64),
     RemoteVerifiedReadonlyFile(VerifiedFileReader<RemoteFileReader, RemoteMerkleTreeReader>, u64),
     RemoteUnverifiedReadonlyFile(RemoteFileReader, u64),
+    RemoteVerifiedNewFile(VerifiedFileEditor<RemoteFileEditor>),
 }
 
 struct AuthFs {
@@ -84,11 +91,20 @@
     }
 }
 
-fn create_stat(ino: libc::ino_t, file_size: u64) -> io::Result<libc::stat64> {
+enum FileMode {
+    ReadOnly,
+    ReadWrite,
+}
+
+fn create_stat(ino: libc::ino_t, file_size: u64, file_mode: FileMode) -> io::Result<libc::stat64> {
     let mut st = unsafe { MaybeUninit::<libc::stat64>::zeroed().assume_init() };
 
     st.st_ino = ino;
-    st.st_mode = libc::S_IFREG | libc::S_IRUSR | libc::S_IRGRP | libc::S_IROTH;
+    st.st_mode = match file_mode {
+        // Until needed, let's just grant the owner access.
+        FileMode::ReadOnly => libc::S_IFREG | libc::S_IRUSR,
+        FileMode::ReadWrite => libc::S_IFREG | libc::S_IRUSR | libc::S_IWUSR,
+    };
     st.st_dev = 0;
     st.st_nlink = 1;
     st.st_uid = 0;
@@ -160,6 +176,12 @@
         self.max_write
     }
 
+    fn init(&self, _capable: FsOptions) -> io::Result<FsOptions> {
+        // Enable writeback cache for better performance especially since our bandwidth to the
+        // backend service is limited.
+        Ok(FsOptions::WRITEBACK_CACHE)
+    }
+
     fn lookup(&self, _ctx: Context, _parent: Inode, name: &CStr) -> io::Result<Entry> {
         // Only accept file name that looks like an integrer. Files in the pool are simply exposed
         // by their inode number. Also, there is currently no directory structure.
@@ -173,7 +195,10 @@
             | FileConfig::LocalUnverifiedReadonlyFile(_, file_size)
             | FileConfig::RemoteUnverifiedReadonlyFile(_, file_size)
             | FileConfig::RemoteVerifiedReadonlyFile(_, file_size) => {
-                create_stat(inode, *file_size)?
+                create_stat(inode, *file_size, FileMode::ReadOnly)?
+            }
+            FileConfig::RemoteVerifiedNewFile(file) => {
+                create_stat(inode, file.size(), FileMode::ReadWrite)?
             }
         };
         Ok(Entry {
@@ -197,7 +222,10 @@
                 | FileConfig::LocalUnverifiedReadonlyFile(_, file_size)
                 | FileConfig::RemoteUnverifiedReadonlyFile(_, file_size)
                 | FileConfig::RemoteVerifiedReadonlyFile(_, file_size) => {
-                    create_stat(inode, *file_size)?
+                    create_stat(inode, *file_size, FileMode::ReadOnly)?
+                }
+                FileConfig::RemoteVerifiedNewFile(file) => {
+                    create_stat(inode, file.size(), FileMode::ReadWrite)?
                 }
             },
             DEFAULT_METADATA_TIMEOUT,
@@ -229,6 +257,11 @@
                 // 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))
+            }
         }
     }
 
@@ -256,6 +289,33 @@
             FileConfig::RemoteUnverifiedReadonlyFile(file, file_size) => {
                 read_chunks(w, file, *file_size, offset, size)
             }
+            FileConfig::RemoteVerifiedNewFile(file) => {
+                // Note that with FsOptions::WRITEBACK_CACHE, it's possible for the kernel to
+                // request a read even if the file is open with O_WRONLY.
+                read_chunks(w, file, file.size(), offset, size)
+            }
+        }
+    }
+
+    fn write<R: io::Read + ZeroCopyReader>(
+        &self,
+        _ctx: Context,
+        inode: Self::Inode,
+        _handle: Self::Handle,
+        mut r: R,
+        size: u32,
+        offset: u64,
+        _lock_owner: Option<u64>,
+        _delayed_write: bool,
+        _flags: u32,
+    ) -> io::Result<usize> {
+        match self.get_file_config(&inode)? {
+            FileConfig::RemoteVerifiedNewFile(file) => {
+                let mut buf = vec![0; size as usize];
+                r.read_exact(&mut buf)?;
+                file.write_at(&buf, offset)
+            }
+            _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
         }
     }
 }