fd_server: support open file by path at a dir fd

When fd_server allows the client/authfs to (only) read a directory, authfs
will be able to serve the shared directory remotely (and implement its own
authentication).

Bug: 203251769
Test: atest AuthFsHostTest

Change-Id: If7209ec496b305ba8f469a382c2f775dcd0d1711
diff --git a/authfs/fd_server/src/aidl.rs b/authfs/fd_server/src/aidl.rs
index 0c41eac..fa1914a 100644
--- a/authfs/fd_server/src/aidl.rs
+++ b/authfs/fd_server/src/aidl.rs
@@ -25,8 +25,8 @@
 use std::fs::File;
 use std::io;
 use std::os::unix::fs::FileExt;
-use std::os::unix::io::{AsRawFd, FromRawFd};
-use std::path::MAIN_SEPARATOR;
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use std::path::{Component, Path, PathBuf, MAIN_SEPARATOR};
 use std::sync::{Arc, Mutex};
 
 use crate::fsverity;
@@ -59,9 +59,11 @@
         file: File,
 
         /// Alternative Merkle tree stored in another file.
+        /// TODO(205987437): Replace with .fsv_meta file.
         alt_merkle_tree: Option<File>,
 
         /// Alternative signature stored in another file.
+        /// TODO(205987437): Replace with .fsv_meta file.
         alt_signature: Option<File>,
     },
 
@@ -69,6 +71,9 @@
     /// regular file and does not have any specific property.
     ReadWrite(File),
 
+    /// A read-only directory to serve by this server.
+    InputDir(Dir),
+
     /// A writable directory to serve by this server.
     OutputDir(Dir),
 }
@@ -132,7 +137,7 @@
                     new_errno_error(Errno::EIO)
                 })
             }
-            FdConfig::OutputDir(_) => Err(new_errno_error(Errno::EISDIR)),
+            FdConfig::InputDir(_) | FdConfig::OutputDir(_) => Err(new_errno_error(Errno::EISDIR)),
         })
     }
 
@@ -165,7 +170,7 @@
                 // use.
                 Err(new_errno_error(Errno::ENOSYS))
             }
-            FdConfig::OutputDir(_) => Err(new_errno_error(Errno::EISDIR)),
+            FdConfig::InputDir(_) | FdConfig::OutputDir(_) => Err(new_errno_error(Errno::EISDIR)),
         })
     }
 
@@ -195,7 +200,7 @@
                 // There is no signature for a writable file.
                 Err(new_errno_error(Errno::ENOSYS))
             }
-            FdConfig::OutputDir(_) => Err(new_errno_error(Errno::EISDIR)),
+            FdConfig::InputDir(_) | FdConfig::OutputDir(_) => Err(new_errno_error(Errno::EISDIR)),
         })
     }
 
@@ -213,7 +218,7 @@
                     new_errno_error(Errno::EIO)
                 })? as i32)
             }
-            FdConfig::OutputDir(_) => Err(new_errno_error(Errno::EISDIR)),
+            FdConfig::InputDir(_) | FdConfig::OutputDir(_) => Err(new_errno_error(Errno::EISDIR)),
         })
     }
 
@@ -229,7 +234,7 @@
                     new_errno_error(Errno::EIO)
                 })
             }
-            FdConfig::OutputDir(_) => Err(new_errno_error(Errno::EISDIR)),
+            FdConfig::InputDir(_) | FdConfig::OutputDir(_) => Err(new_errno_error(Errno::EISDIR)),
         })
     }
 
@@ -254,7 +259,31 @@
                 // for a writable file.
                 Err(new_errno_error(Errno::ENOSYS))
             }
-            FdConfig::OutputDir(_) => Err(new_errno_error(Errno::EISDIR)),
+            FdConfig::InputDir(_) | FdConfig::OutputDir(_) => Err(new_errno_error(Errno::EISDIR)),
+        })
+    }
+
+    fn openFileInDirectory(&self, fd: i32, file_path: &str) -> BinderResult<i32> {
+        let path_buf = PathBuf::from(file_path);
+        // Checks if the path is a simple, related path.
+        if path_buf.components().any(|c| !matches!(c, Component::Normal(_))) {
+            return Err(new_errno_error(Errno::EINVAL));
+        }
+
+        self.insert_new_fd(fd, |config| match config {
+            FdConfig::InputDir(dir) => {
+                let file = open_readonly_at(dir.as_raw_fd(), &path_buf).map_err(new_errno_error)?;
+
+                // TODO(205987437): Provide the corresponding ".fsv_meta" file when it's created.
+                Ok((
+                    file.as_raw_fd(),
+                    FdConfig::Readonly { file, alt_merkle_tree: None, alt_signature: None },
+                ))
+            }
+            FdConfig::OutputDir(_) => {
+                Err(new_errno_error(Errno::ENOSYS)) // TODO: Implement when needed
+            }
+            _ => Err(new_errno_error(Errno::ENOTDIR)),
         })
     }
 
@@ -263,6 +292,7 @@
             return Err(new_errno_error(Errno::EINVAL));
         }
         self.insert_new_fd(fd, |config| match config {
+            FdConfig::InputDir(_) => Err(new_errno_error(Errno::EACCES)),
             FdConfig::OutputDir(dir) => {
                 let new_fd = openat(
                     dir.as_raw_fd(),
@@ -286,6 +316,7 @@
             return Err(new_errno_error(Errno::EINVAL));
         }
         self.insert_new_fd(dir_fd, |config| match config {
+            FdConfig::InputDir(_) => Err(new_errno_error(Errno::EACCES)),
             FdConfig::OutputDir(_) => {
                 mkdirat(dir_fd, basename, Mode::S_IRWXU).map_err(new_errno_error)?;
                 let new_dir = Dir::openat(
@@ -313,3 +344,10 @@
 fn new_errno_error(errno: Errno) -> Status {
     new_binder_service_specific_error(errno as i32, errno.desc())
 }
+
+fn open_readonly_at(dir_fd: RawFd, path: &Path) -> nix::Result<File> {
+    let new_fd = openat(dir_fd, path, OFlag::O_RDONLY, Mode::empty())?;
+    // SAFETY: new_fd is just created successfully and not owned.
+    let new_file = unsafe { File::from_raw_fd(new_fd) };
+    Ok(new_file)
+}