authfs: Remote file editor over binder

The remote editor allows to read/write from/to a remote file. This
change:
 - Adds new binder API `writeFile`.
 - Update fd_server to serve a read/writable fd. Read operation is done
   through the existing read API when applicable.
 - Adds `RemoteFileEditor` as a binder client for both read and write.

Bug: 171279640
Test: adb shell exec 9<>/data/local/tmp/output fd_server --rw-fds 9
Test: with changes in fusefs.rs, saw file changed correctly (md5sum)

Change-Id: I78ef198ee8a3a0f2d99717dc0c00fccde757f3de
diff --git a/authfs/src/remote_file.rs b/authfs/src/remote_file.rs
index 01e803c..ed7381c 100644
--- a/authfs/src/remote_file.rs
+++ b/authfs/src/remote_file.rs
@@ -21,6 +21,7 @@
 
 use crate::common::CHUNK_SIZE;
 use crate::reader::ReadOnlyDataByChunk;
+use crate::writer::RandomWrite;
 
 use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService;
 use authfs_aidl_interface::binder::Strong;
@@ -36,6 +37,23 @@
     }
 }
 
+fn remote_read_chunk(
+    service: &Arc<Mutex<VirtFdService>>,
+    remote_fd: i32,
+    chunk_index: u64,
+    mut buf: &mut [u8],
+) -> io::Result<usize> {
+    let offset = i64::try_from(chunk_index * CHUNK_SIZE)
+        .map_err(|_| io::Error::from_raw_os_error(libc::EOVERFLOW))?;
+
+    let chunk = service
+        .lock()
+        .unwrap()
+        .readFile(remote_fd, offset, buf.len() as i32)
+        .map_err(|e| io::Error::new(io::ErrorKind::Other, e.get_description()))?;
+    buf.write(&chunk)
+}
+
 pub struct RemoteChunkedFileReader {
     // This needs to have Sync trait to be used in fuse::worker::start_message_loop.
     service: Arc<Mutex<VirtFdService>>,
@@ -49,17 +67,8 @@
 }
 
 impl ReadOnlyDataByChunk for RemoteChunkedFileReader {
-    fn read_chunk(&self, chunk_index: u64, mut buf: &mut [u8]) -> io::Result<usize> {
-        let offset = i64::try_from(chunk_index * CHUNK_SIZE)
-            .map_err(|_| io::Error::from_raw_os_error(libc::EOVERFLOW))?;
-
-        let service = Arc::clone(&self.service);
-        let chunk = service
-            .lock()
-            .unwrap()
-            .readFile(self.file_fd, offset, buf.len() as i32)
-            .map_err(|e| io::Error::new(io::ErrorKind::Other, e.get_description()))?;
-        buf.write(&chunk)
+    fn read_chunk(&self, chunk_index: u64, buf: &mut [u8]) -> io::Result<usize> {
+        remote_read_chunk(&self.service, self.file_fd, chunk_index, buf)
     }
 }
 
@@ -81,8 +90,8 @@
         let offset = i64::try_from(chunk_index * CHUNK_SIZE)
             .map_err(|_| io::Error::from_raw_os_error(libc::EOVERFLOW))?;
 
-        let service = Arc::clone(&self.service);
-        let chunk = service
+        let chunk = self
+            .service
             .lock()
             .unwrap()
             .readFsverityMerkleTree(self.file_fd, offset, buf.len() as i32)
@@ -90,3 +99,36 @@
         buf.write(&chunk)
     }
 }
+
+pub struct RemoteFileEditor {
+    // This needs to have Sync trait to be used in fuse::worker::start_message_loop.
+    service: Arc<Mutex<VirtFdService>>,
+    file_fd: i32,
+}
+
+impl RemoteFileEditor {
+    #[allow(dead_code)]
+    pub fn new(service: Arc<Mutex<VirtFdService>>, file_fd: i32) -> Self {
+        RemoteFileEditor { service, file_fd }
+    }
+}
+
+impl RandomWrite for RemoteFileEditor {
+    fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
+        let offset =
+            i64::try_from(offset).map_err(|_| io::Error::from_raw_os_error(libc::EOVERFLOW))?;
+        let size = self
+            .service
+            .lock()
+            .unwrap()
+            .writeFile(self.file_fd, &buf, offset)
+            .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
+    }
+}
+
+impl ReadOnlyDataByChunk for RemoteFileEditor {
+    fn read_chunk(&self, chunk_index: u64, buf: &mut [u8]) -> io::Result<usize> {
+        remote_read_chunk(&self.service, self.file_fd, chunk_index, buf)
+    }
+}