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/aidl/com/android/virt/fs/IVirtFdService.aidl b/authfs/aidl/com/android/virt/fs/IVirtFdService.aidl
index 58ccfc3..bf4ac61 100644
--- a/authfs/aidl/com/android/virt/fs/IVirtFdService.aidl
+++ b/authfs/aidl/com/android/virt/fs/IVirtFdService.aidl
@@ -57,12 +57,20 @@
long getFileSize(int fd);
/**
+ * Open a file given the remote directory FD.
+ *
+ * @param pathname The file path to open. Must be a related path.
+ * @return file A remote FD that represents the opened file.
+ */
+ int openFileInDirectory(int dirFd, String pathname);
+
+ /**
* Create a file given the remote directory FD.
*
* @param basename The file name to create. Must not contain directory separator.
* @return file A remote FD that represents the new created file.
*/
- int createFileInDirectory(int fd, String basename);
+ int createFileInDirectory(int dirFd, String basename);
/**
* Create a directory inside the given remote directory FD.
@@ -70,5 +78,5 @@
* @param basename The directory name to create. Must not contain directory separator.
* @return file FD that represents the new created directory.
*/
- int createDirectoryInDirectory(int id, String basename);
+ int createDirectoryInDirectory(int dirFd, String basename);
}
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)
+}
diff --git a/authfs/fd_server/src/main.rs b/authfs/fd_server/src/main.rs
index bbcd49f..f5a3cba 100644
--- a/authfs/fd_server/src/main.rs
+++ b/authfs/fd_server/src/main.rs
@@ -78,9 +78,13 @@
Ok((fd, FdConfig::ReadWrite(file)))
}
+fn parse_arg_ro_dirs(arg: &str) -> Result<(i32, FdConfig)> {
+ let fd = arg.parse::<i32>()?;
+ Ok((fd, FdConfig::InputDir(Dir::from_fd(fd)?)))
+}
+
fn parse_arg_rw_dirs(arg: &str) -> Result<(i32, FdConfig)> {
let fd = arg.parse::<i32>()?;
-
Ok((fd, FdConfig::OutputDir(Dir::from_fd(fd)?)))
}
@@ -100,6 +104,10 @@
.long("rw-fds")
.multiple(true)
.number_of_values(1))
+ .arg(clap::Arg::with_name("ro-dirs")
+ .long("ro-dirs")
+ .multiple(true)
+ .number_of_values(1))
.arg(clap::Arg::with_name("rw-dirs")
.long("rw-dirs")
.multiple(true)
@@ -122,6 +130,12 @@
fd_pool.insert(fd, config);
}
}
+ if let Some(args) = matches.values_of("ro-dirs") {
+ for arg in args {
+ let (fd, config) = parse_arg_ro_dirs(arg)?;
+ fd_pool.insert(fd, config);
+ }
+ }
if let Some(args) = matches.values_of("rw-dirs") {
for arg in args {
let (fd, config) = parse_arg_rw_dirs(arg)?;