Support setting file mode
File mode of writable AuthFsEntry can now be changed. The mode is
maintain privately in authfs, but also pass through to fd_server.
Note that this change only aims to support getting/setting the file
mode. The mode is not currently used for ACL check.
Bug: 205169366
Test: atest AuthFsHostTest
Test: atest ComposHostTestCases
Test: composd_cmd forced-odrefresh
# exit 80 without ART hack, with permissive SELinux
Change-Id: I2405baedae9ba2be5e84eb84d3228f7be080f8c6
diff --git a/authfs/src/file.rs b/authfs/src/file.rs
index 6353209..9bbf3ef 100644
--- a/authfs/src/file.rs
+++ b/authfs/src/file.rs
@@ -1,6 +1,8 @@
+mod attr;
mod dir;
mod remote_file;
+pub use attr::Attr;
pub use dir::{InMemoryDir, RemoteDirEditor};
pub use remote_file::{RemoteFileEditor, RemoteFileReader, RemoteMerkleTreeReader};
diff --git a/authfs/src/file/attr.rs b/authfs/src/file/attr.rs
new file mode 100644
index 0000000..48084aa
--- /dev/null
+++ b/authfs/src/file/attr.rs
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use log::error;
+use nix::sys::stat::{mode_t, Mode, SFlag};
+use std::io;
+
+use super::VirtFdService;
+
+/// Default/assumed mode of files not created by authfs.
+///
+/// For files that are given to authfs as FDs (i.e. not created through authfs), their mode is
+/// unknown (or untrusted) until it is ever set. The default mode is just to make it
+/// readable/writable to VFS. When the mode is set, the value on fd_server is supposed to become
+/// consistent.
+const DEFAULT_FILE_MODE: Mode =
+ Mode::from_bits_truncate(Mode::S_IRUSR.bits() | Mode::S_IWUSR.bits());
+
+/// Default/assumed mode of directories not created by authfs.
+///
+/// See above.
+const DEFAULT_DIR_MODE: Mode = Mode::S_IRWXU;
+
+/// `Attr` maintains the local truth for attributes (e.g. mode and type) while allowing setting the
+/// remote attribute for the file description.
+pub struct Attr {
+ service: VirtFdService,
+ mode: Mode,
+ remote_fd: i32,
+ is_dir: bool,
+}
+
+impl Attr {
+ pub fn new_file(service: VirtFdService, remote_fd: i32) -> Attr {
+ Attr { service, mode: DEFAULT_FILE_MODE, remote_fd, is_dir: false }
+ }
+
+ pub fn new_dir(service: VirtFdService, remote_fd: i32) -> Attr {
+ Attr { service, mode: DEFAULT_DIR_MODE, remote_fd, is_dir: true }
+ }
+
+ pub fn new_file_with_mode(service: VirtFdService, remote_fd: i32, mode: mode_t) -> Attr {
+ Attr { service, mode: Mode::from_bits_truncate(mode), remote_fd, is_dir: false }
+ }
+
+ pub fn new_dir_with_mode(service: VirtFdService, remote_fd: i32, mode: mode_t) -> Attr {
+ Attr { service, mode: Mode::from_bits_truncate(mode), remote_fd, is_dir: true }
+ }
+
+ pub fn mode(&self) -> u32 {
+ self.mode.bits()
+ }
+
+ /// Sets the file mode.
+ ///
+ /// In addition to the actual file mode, `encoded_mode` also contains information of the file
+ /// type.
+ pub fn set_mode(&mut self, encoded_mode: u32) -> io::Result<()> {
+ let new_sflag = SFlag::from_bits_truncate(encoded_mode);
+ let new_mode = Mode::from_bits_truncate(encoded_mode);
+
+ let type_flag = if self.is_dir { SFlag::S_IFDIR } else { SFlag::S_IFREG };
+ if !type_flag.contains(new_sflag) {
+ return Err(io::Error::from_raw_os_error(libc::EINVAL));
+ }
+
+ // Request for update only if changing.
+ if new_mode != self.mode {
+ self.service.chmod(self.remote_fd, new_mode.bits() as i32).map_err(|e| {
+ error!(
+ "Failed to chmod (fd: {}, mode: {:o}) on fd_server: {:?}",
+ self.remote_fd, new_mode, e
+ );
+ io::Error::from_raw_os_error(libc::EIO)
+ })?;
+ self.mode = new_mode;
+ }
+ Ok(())
+ }
+}
diff --git a/authfs/src/file/dir.rs b/authfs/src/file/dir.rs
index 2a8f359..7f26fd7 100644
--- a/authfs/src/file/dir.rs
+++ b/authfs/src/file/dir.rs
@@ -15,10 +15,12 @@
*/
use log::warn;
+use nix::sys::stat::Mode;
use std::collections::{hash_map, HashMap};
use std::io;
use std::path::{Path, PathBuf};
+use super::attr::Attr;
use super::remote_file::RemoteFileEditor;
use super::{validate_basename, VirtFdService, VirtFdServiceStatus};
use crate::fsverity::VerifiedFileEditor;
@@ -74,37 +76,43 @@
&mut self,
basename: &Path,
inode: Inode,
- ) -> io::Result<VerifiedFileEditor<RemoteFileEditor>> {
- self.validate_argument(basename)?;
-
+ mode: libc::mode_t,
+ ) -> io::Result<(VerifiedFileEditor<RemoteFileEditor>, Attr)> {
+ let mode = self.validate_arguments(basename, mode)?;
let basename_str =
basename.to_str().ok_or_else(|| io::Error::from_raw_os_error(libc::EINVAL))?;
let new_fd = self
.service
- .createFileInDirectory(self.remote_dir_fd, basename_str)
+ .createFileInDirectory(self.remote_dir_fd, basename_str, mode as i32)
.map_err(into_io_error)?;
let new_remote_file =
VerifiedFileEditor::new(RemoteFileEditor::new(self.service.clone(), new_fd));
self.entries.insert(basename.to_path_buf(), DirEntry { inode, is_dir: false });
- Ok(new_remote_file)
+ let new_attr = Attr::new_file_with_mode(self.service.clone(), new_fd, mode);
+ Ok((new_remote_file, new_attr))
}
/// Creates a remote directory named `basename` with corresponding `inode` at the current
/// directory.
- pub fn mkdir(&mut self, basename: &Path, inode: Inode) -> io::Result<RemoteDirEditor> {
- self.validate_argument(basename)?;
-
+ pub fn mkdir(
+ &mut self,
+ basename: &Path,
+ inode: Inode,
+ mode: libc::mode_t,
+ ) -> io::Result<(RemoteDirEditor, Attr)> {
+ let mode = self.validate_arguments(basename, mode)?;
let basename_str =
basename.to_str().ok_or_else(|| io::Error::from_raw_os_error(libc::EINVAL))?;
let new_fd = self
.service
- .createDirectoryInDirectory(self.remote_dir_fd, basename_str)
+ .createDirectoryInDirectory(self.remote_dir_fd, basename_str, mode as i32)
.map_err(into_io_error)?;
let new_remote_dir = RemoteDirEditor::new(self.service.clone(), new_fd);
self.entries.insert(basename.to_path_buf(), DirEntry { inode, is_dir: true });
- Ok(new_remote_dir)
+ let new_attr = Attr::new_dir_with_mode(self.service.clone(), new_fd, mode);
+ Ok((new_remote_dir, new_attr))
}
/// Deletes a file
@@ -167,17 +175,19 @@
}
}
- fn validate_argument(&self, basename: &Path) -> io::Result<()> {
+ fn validate_arguments(&self, basename: &Path, mode: u32) -> io::Result<u32> {
// Kernel should only give us a basename.
debug_assert!(validate_basename(basename).is_ok());
if self.entries.contains_key(basename) {
- Err(io::Error::from_raw_os_error(libc::EEXIST))
- } else if self.entries.len() >= MAX_ENTRIES.into() {
- Err(io::Error::from_raw_os_error(libc::EMLINK))
- } else {
- Ok(())
+ return Err(io::Error::from_raw_os_error(libc::EEXIST));
}
+
+ if self.entries.len() >= MAX_ENTRIES.into() {
+ return Err(io::Error::from_raw_os_error(libc::EMLINK));
+ }
+
+ Ok(Mode::from_bits_truncate(mode).bits())
}
}
diff --git a/authfs/src/fusefs.rs b/authfs/src/fusefs.rs
index 17a368f..85a8371 100644
--- a/authfs/src/fusefs.rs
+++ b/authfs/src/fusefs.rs
@@ -37,8 +37,8 @@
use crate::common::{divide_roundup, ChunkedSizeIter, CHUNK_SIZE};
use crate::file::{
- validate_basename, InMemoryDir, RandomWrite, ReadByChunk, RemoteDirEditor, RemoteFileEditor,
- RemoteFileReader, RemoteMerkleTreeReader,
+ validate_basename, Attr, InMemoryDir, RandomWrite, ReadByChunk, RemoteDirEditor,
+ RemoteFileEditor, RemoteFileReader, RemoteMerkleTreeReader,
};
use crate::fsstat::RemoteFsStatsReader;
use crate::fsverity::{VerifiedFileEditor, VerifiedFileReader};
@@ -66,16 +66,16 @@
UnverifiedReadonly { reader: RemoteFileReader, file_size: u64 },
/// A file type that is initially empty, and the content is stored on a remote server. File
/// integrity is guaranteed with private Merkle tree.
- VerifiedNew { editor: VerifiedFileEditor<RemoteFileEditor> },
+ VerifiedNew { editor: VerifiedFileEditor<RemoteFileEditor>, attr: Attr },
/// A directory type that is initially empty. One can create new file (`VerifiedNew`) and new
/// directory (`VerifiedNewDirectory` itself) with integrity guaranteed within the VM.
- VerifiedNewDirectory { dir: RemoteDirEditor },
+ VerifiedNewDirectory { dir: RemoteDirEditor, attr: Attr },
}
impl AuthFsEntry {
- fn expect_empty_writable_directory(&self) -> io::Result<()> {
+ fn expect_empty_deletable_directory(&self) -> io::Result<()> {
match self {
- AuthFsEntry::VerifiedNewDirectory { dir } => {
+ AuthFsEntry::VerifiedNewDirectory { dir, .. } => {
if dir.number_of_entries() == 0 {
Ok(())
} else {
@@ -304,7 +304,7 @@
#[allow(clippy::enum_variant_names)]
enum AccessMode {
ReadOnly,
- ReadWrite,
+ Variable(u32),
}
fn create_stat(
@@ -317,10 +317,11 @@
st.st_ino = ino;
st.st_mode = match access_mode {
- // Until needed, let's just grant the owner access.
- // TODO(205169366): Implement mode properly.
- AccessMode::ReadOnly => libc::S_IFREG | libc::S_IRUSR,
- AccessMode::ReadWrite => libc::S_IFREG | libc::S_IRUSR | libc::S_IWUSR,
+ AccessMode::ReadOnly => {
+ // Until needed, let's just grant the owner access.
+ libc::S_IFREG | libc::S_IRUSR
+ }
+ AccessMode::Variable(mode) => libc::S_IFREG | mode,
};
st.st_nlink = 1;
st.st_uid = 0;
@@ -334,18 +335,22 @@
Ok(st)
}
-fn create_dir_stat(ino: libc::ino_t, file_number: u16) -> io::Result<libc::stat64> {
+fn create_dir_stat(
+ ino: libc::ino_t,
+ file_number: u16,
+ access_mode: AccessMode,
+) -> io::Result<libc::stat64> {
// SAFETY: stat64 is a plan C struct without pointer.
let mut st = unsafe { MaybeUninit::<libc::stat64>::zeroed().assume_init() };
st.st_ino = ino;
- // TODO(205169366): Implement mode properly.
- st.st_mode = libc::S_IFDIR
- | libc::S_IXUSR
- | libc::S_IWUSR
- | libc::S_IRUSR
- | libc::S_IXGRP
- | libc::S_IXOTH;
+ st.st_mode = match access_mode {
+ AccessMode::ReadOnly => {
+ // Until needed, let's just grant the owner access and search to group and others.
+ libc::S_IFDIR | libc::S_IXUSR | libc::S_IRUSR | libc::S_IXGRP | libc::S_IXOTH
+ }
+ AccessMode::Variable(mode) => libc::S_IFDIR | mode,
+ };
// 2 extra for . and ..
st.st_nlink = file_number
@@ -431,7 +436,7 @@
let path = cstr_to_path(name);
dir.lookup_inode(path).ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))
}
- AuthFsEntry::VerifiedNewDirectory { dir } => {
+ AuthFsEntry::VerifiedNewDirectory { dir, .. } => {
let path = cstr_to_path(name);
dir.find_inode(path)
}
@@ -445,18 +450,20 @@
|InodeState { entry, handle_ref_count, .. }| {
let st = match entry {
AuthFsEntry::ReadonlyDirectory { dir } => {
- create_dir_stat(inode, dir.number_of_entries())
+ create_dir_stat(inode, dir.number_of_entries(), AccessMode::ReadOnly)
}
AuthFsEntry::UnverifiedReadonly { file_size, .. }
| AuthFsEntry::VerifiedReadonly { file_size, .. } => {
create_stat(inode, *file_size, AccessMode::ReadOnly)
}
- AuthFsEntry::VerifiedNew { editor } => {
- create_stat(inode, editor.size(), AccessMode::ReadWrite)
+ AuthFsEntry::VerifiedNew { editor, attr, .. } => {
+ create_stat(inode, editor.size(), AccessMode::Variable(attr.mode()))
}
- AuthFsEntry::VerifiedNewDirectory { dir } => {
- create_dir_stat(inode, dir.number_of_entries())
- }
+ AuthFsEntry::VerifiedNewDirectory { dir, attr } => create_dir_stat(
+ inode,
+ dir.number_of_entries(),
+ AccessMode::Variable(attr.mode()),
+ ),
}?;
*handle_ref_count += 1;
Ok(st)
@@ -514,18 +521,20 @@
Ok((
match config {
AuthFsEntry::ReadonlyDirectory { dir } => {
- create_dir_stat(inode, dir.number_of_entries())
+ create_dir_stat(inode, dir.number_of_entries(), AccessMode::ReadOnly)
}
AuthFsEntry::UnverifiedReadonly { file_size, .. }
| AuthFsEntry::VerifiedReadonly { file_size, .. } => {
create_stat(inode, *file_size, AccessMode::ReadOnly)
}
- AuthFsEntry::VerifiedNew { editor } => {
- create_stat(inode, editor.size(), AccessMode::ReadWrite)
+ AuthFsEntry::VerifiedNew { editor, attr, .. } => {
+ create_stat(inode, editor.size(), AccessMode::Variable(attr.mode()))
}
- AuthFsEntry::VerifiedNewDirectory { dir } => {
- create_dir_stat(inode, dir.number_of_entries())
- }
+ AuthFsEntry::VerifiedNewDirectory { dir, attr } => create_dir_stat(
+ inode,
+ dir.number_of_entries(),
+ AccessMode::Variable(attr.mode()),
+ ),
}?,
DEFAULT_METADATA_TIMEOUT,
))
@@ -546,8 +555,8 @@
check_access_mode(flags, libc::O_RDONLY)?;
}
AuthFsEntry::VerifiedNew { .. } => {
- // No need to check access modes since all the modes are allowed to the
- // read-writable file.
+ // TODO(victorhsieh): Imeplement ACL check using the attr and ctx. Always allow
+ // for now.
}
AuthFsEntry::ReadonlyDirectory { .. }
| AuthFsEntry::VerifiedNewDirectory { .. } => {
@@ -566,22 +575,22 @@
_ctx: Context,
parent: Self::Inode,
name: &CStr,
- _mode: u32,
+ mode: u32,
_flags: u32,
- _umask: u32,
+ umask: u32,
) -> io::Result<(Entry, Option<Self::Handle>, fuse::sys::OpenOptions)> {
- // TODO(205169366): Implement mode properly.
// TODO(205172873): handle O_TRUNC and O_EXCL properly.
let new_inode = self.create_new_entry_with_ref_count(
parent,
name,
|parent_entry, basename, new_inode| match parent_entry {
- AuthFsEntry::VerifiedNewDirectory { dir } => {
+ AuthFsEntry::VerifiedNewDirectory { dir, .. } => {
if dir.has_entry(basename) {
return Err(io::Error::from_raw_os_error(libc::EEXIST));
}
- let new_file = dir.create_file(basename, new_inode)?;
- Ok(AuthFsEntry::VerifiedNew { editor: new_file })
+ let mode = mode & !umask;
+ let (new_file, new_attr) = dir.create_file(basename, new_inode, mode)?;
+ Ok(AuthFsEntry::VerifiedNew { editor: new_file, attr: new_attr })
}
_ => Err(io::Error::from_raw_os_error(libc::EBADF)),
},
@@ -591,7 +600,7 @@
Entry {
inode: new_inode,
generation: 0,
- attr: create_stat(new_inode, /* file_size */ 0, AccessMode::ReadWrite)?,
+ attr: create_stat(new_inode, /* file_size */ 0, AccessMode::Variable(mode))?,
entry_timeout: DEFAULT_METADATA_TIMEOUT,
attr_timeout: DEFAULT_METADATA_TIMEOUT,
},
@@ -620,12 +629,15 @@
AuthFsEntry::UnverifiedReadonly { reader, file_size } => {
read_chunks(w, reader, *file_size, offset, size)
}
- AuthFsEntry::VerifiedNew { editor } => {
+ AuthFsEntry::VerifiedNew { editor, .. } => {
// 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, editor, editor.size(), offset, size)
}
- _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
+ AuthFsEntry::ReadonlyDirectory { .. }
+ | AuthFsEntry::VerifiedNewDirectory { .. } => {
+ Err(io::Error::from_raw_os_error(libc::EISDIR))
+ }
}
})
}
@@ -643,12 +655,17 @@
_flags: u32,
) -> io::Result<usize> {
self.handle_inode(&inode, |config| match config {
- AuthFsEntry::VerifiedNew { editor } => {
+ AuthFsEntry::VerifiedNew { editor, .. } => {
let mut buf = vec![0; size as usize];
r.read_exact(&mut buf)?;
editor.write_at(&buf, offset)
}
- _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
+ AuthFsEntry::VerifiedReadonly { .. } | AuthFsEntry::UnverifiedReadonly { .. } => {
+ Err(io::Error::from_raw_os_error(libc::EPERM))
+ }
+ AuthFsEntry::ReadonlyDirectory { .. } | AuthFsEntry::VerifiedNewDirectory { .. } => {
+ Err(io::Error::from_raw_os_error(libc::EISDIR))
+ }
})
}
@@ -656,55 +673,51 @@
&self,
_ctx: Context,
inode: Inode,
- attr: libc::stat64,
+ in_attr: libc::stat64,
_handle: Option<Handle>,
valid: SetattrValid,
) -> io::Result<(libc::stat64, Duration)> {
- self.handle_inode(&inode, |config| {
- match config {
- AuthFsEntry::VerifiedNew { editor } => {
- // Initialize the default stat.
- let mut new_attr = create_stat(inode, editor.size(), AccessMode::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)?;
- }
+ let mut inode_table = self.inode_table.lock().unwrap();
+ handle_inode_mut_locked(&mut inode_table, &inode, |InodeState { entry, .. }| match entry {
+ AuthFsEntry::VerifiedNew { editor, attr } => {
+ check_unsupported_setattr_request(valid)?;
- 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))
+ // Initialize the default stat.
+ let mut new_attr =
+ create_stat(inode, editor.size(), AccessMode::Variable(attr.mode()))?;
+ // `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!(in_attr.st_size >= 0);
+ new_attr.st_size = in_attr.st_size;
+ editor.resize(in_attr.st_size as u64)?;
}
- _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
+ if valid.contains(SetattrValid::MODE) {
+ attr.set_mode(in_attr.st_mode)?;
+ new_attr.st_mode = in_attr.st_mode;
+ }
+ Ok((new_attr, DEFAULT_METADATA_TIMEOUT))
}
+ AuthFsEntry::VerifiedNewDirectory { dir, attr } => {
+ check_unsupported_setattr_request(valid)?;
+ if valid.contains(SetattrValid::SIZE) {
+ return Err(io::Error::from_raw_os_error(libc::EISDIR));
+ }
+
+ // Initialize the default stat.
+ let mut new_attr = create_dir_stat(
+ inode,
+ dir.number_of_entries(),
+ AccessMode::Variable(attr.mode()),
+ )?;
+ if valid.contains(SetattrValid::MODE) {
+ attr.set_mode(in_attr.st_mode)?;
+ new_attr.st_mode = in_attr.st_mode;
+ }
+ Ok((new_attr, DEFAULT_METADATA_TIMEOUT))
+ }
+ _ => Err(io::Error::from_raw_os_error(libc::EPERM)),
})
}
@@ -717,7 +730,7 @@
) -> io::Result<GetxattrReply> {
self.handle_inode(&inode, |config| {
match config {
- AuthFsEntry::VerifiedNew { editor } => {
+ AuthFsEntry::VerifiedNew { editor, .. } => {
// FUSE ioctl is limited, thus we can't implement fs-verity ioctls without a kernel
// change (see b/196635431). Until it's possible, use xattr to expose what we need
// as an authfs specific API.
@@ -747,20 +760,20 @@
_ctx: Context,
parent: Self::Inode,
name: &CStr,
- _mode: u32,
- _umask: u32,
+ mode: u32,
+ umask: u32,
) -> io::Result<Entry> {
- // TODO(205169366): Implement mode properly.
let new_inode = self.create_new_entry_with_ref_count(
parent,
name,
|parent_entry, basename, new_inode| match parent_entry {
- AuthFsEntry::VerifiedNewDirectory { dir } => {
+ AuthFsEntry::VerifiedNewDirectory { dir, .. } => {
if dir.has_entry(basename) {
return Err(io::Error::from_raw_os_error(libc::EEXIST));
}
- let new_dir = dir.mkdir(basename, new_inode)?;
- Ok(AuthFsEntry::VerifiedNewDirectory { dir: new_dir })
+ let mode = mode & !umask;
+ let (new_dir, new_attr) = dir.mkdir(basename, new_inode, mode)?;
+ Ok(AuthFsEntry::VerifiedNewDirectory { dir: new_dir, attr: new_attr })
}
AuthFsEntry::ReadonlyDirectory { .. } => {
Err(io::Error::from_raw_os_error(libc::EACCES))
@@ -772,7 +785,7 @@
Ok(Entry {
inode: new_inode,
generation: 0,
- attr: create_dir_stat(new_inode, /* file_number */ 0)?,
+ attr: create_dir_stat(new_inode, /* file_number */ 0, AccessMode::Variable(mode))?,
entry_timeout: DEFAULT_METADATA_TIMEOUT,
attr_timeout: DEFAULT_METADATA_TIMEOUT,
})
@@ -784,7 +797,7 @@
&mut inode_table,
&parent,
|InodeState { entry, unlinked, .. }| match entry {
- AuthFsEntry::VerifiedNewDirectory { dir } => {
+ AuthFsEntry::VerifiedNewDirectory { dir, .. } => {
let basename: &Path = cstr_to_path(name);
// Delete the file from in both the local and remote directories.
let _inode = dir.delete_file(basename)?;
@@ -810,11 +823,11 @@
// Check before actual removal, with readonly borrow.
handle_inode_locked(&inode_table, &parent, |inode_state| match &inode_state.entry {
- AuthFsEntry::VerifiedNewDirectory { dir } => {
+ AuthFsEntry::VerifiedNewDirectory { dir, .. } => {
let basename: &Path = cstr_to_path(name);
let existing_inode = dir.find_inode(basename)?;
handle_inode_locked(&inode_table, &existing_inode, |inode_state| {
- inode_state.entry.expect_empty_writable_directory()
+ inode_state.entry.expect_empty_deletable_directory()
})
}
AuthFsEntry::ReadonlyDirectory { .. } => {
@@ -829,7 +842,7 @@
&mut inode_table,
&parent,
|InodeState { entry, unlinked, .. }| match entry {
- AuthFsEntry::VerifiedNewDirectory { dir } => {
+ AuthFsEntry::VerifiedNewDirectory { dir, .. } => {
let basename: &Path = cstr_to_path(name);
let _inode = dir.force_delete_directory(basename)?;
*unlinked = true;
@@ -897,6 +910,27 @@
}
}
+fn check_unsupported_setattr_request(valid: SetattrValid) -> io::Result<()> {
+ 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.intersects(
+ SetattrValid::CTIME
+ | SetattrValid::ATIME
+ | SetattrValid::ATIME_NOW
+ | SetattrValid::MTIME
+ | SetattrValid::MTIME_NOW,
+ ) {
+ debug!("Ignoring ctime/atime/mtime change as authfs does not maintain timestamp currently");
+ }
+ Ok(())
+}
+
fn cstr_to_path(cstr: &CStr) -> &Path {
OsStr::from_bytes(cstr.to_bytes()).as_ref()
}
diff --git a/authfs/src/main.rs b/authfs/src/main.rs
index a083381..421cc02 100644
--- a/authfs/src/main.rs
+++ b/authfs/src/main.rs
@@ -43,7 +43,7 @@
use auth::FakeAuthenticator;
use file::{
- InMemoryDir, RemoteDirEditor, RemoteFileEditor, RemoteFileReader, RemoteMerkleTreeReader,
+ Attr, InMemoryDir, RemoteDirEditor, RemoteFileEditor, RemoteFileReader, RemoteMerkleTreeReader,
};
use fsstat::RemoteFsStatsReader;
use fsverity::{VerifiedFileEditor, VerifiedFileReader};
@@ -194,16 +194,20 @@
service: file::VirtFdService,
remote_fd: i32,
) -> Result<AuthFsEntry> {
- let remote_file = RemoteFileEditor::new(service, remote_fd);
- Ok(AuthFsEntry::VerifiedNew { editor: VerifiedFileEditor::new(remote_file) })
+ let remote_file = RemoteFileEditor::new(service.clone(), remote_fd);
+ Ok(AuthFsEntry::VerifiedNew {
+ editor: VerifiedFileEditor::new(remote_file),
+ attr: Attr::new_file(service, remote_fd),
+ })
}
fn new_remote_new_verified_dir_entry(
service: file::VirtFdService,
remote_fd: i32,
) -> Result<AuthFsEntry> {
- let dir = RemoteDirEditor::new(service, remote_fd);
- Ok(AuthFsEntry::VerifiedNewDirectory { dir })
+ let dir = RemoteDirEditor::new(service.clone(), remote_fd);
+ let attr = Attr::new_dir(service, remote_fd);
+ Ok(AuthFsEntry::VerifiedNewDirectory { dir, attr })
}
fn prepare_root_dir_entries(