Normalize inactive State Anchor work storage

This commit is contained in:
ms2k 2026-07-23 21:44:16 +09:00
commit 42e197510e
3 changed files with 133 additions and 11 deletions

View file

@ -1,7 +1,8 @@
use super::storage::{
descriptor_child_path, descriptor_path, inherit_for_exec, open_directory,
clear_work_directory, descriptor_child_path, descriptor_path, inherit_for_exec, open_directory,
open_exact_directory_path, open_mounted_directory, overlay_options, path_from_host,
reopen_inherited_directory, require_directory_descriptor_identity, rootless_unmount_path,
reopen_inherited_directory, require_directory_descriptor_identity, require_same_descriptor,
rootless_unmount_path,
};
use super::{
AsFd, AsRawFd, FileType, HostProcessOwnershipSlot, Instant, MOUNT_POLL_INTERVAL, MOUNT_TIMEOUT,
@ -161,6 +162,7 @@ pub struct RuntimeStateAnchorAuthority {
lower: OwnedFd,
persistent_anchor: OwnedFd,
upper: OwnedFd,
work: OwnedFd,
runtime_anchor: OwnedFd,
merged_label: PathBuf,
merged_descriptor: OwnedFd,
@ -233,13 +235,14 @@ impl RuntimeStateAnchorAuthority {
inherited.runtime_anchor_label,
(retained.st_dev, retained.st_ino),
)?;
drop(open_directory(&persistent_anchor, Path::new("work"))?);
let work = open_directory(&persistent_anchor, Path::new("work"))?;
let merged_descriptor = open_directory(&runtime_anchor, Path::new("merged"))?;
let merged_label = inherited.runtime_anchor_label.join("merged");
Ok(Self {
lower,
persistent_anchor,
upper,
work,
runtime_anchor,
merged_label,
merged_descriptor,
@ -495,6 +498,9 @@ impl RuntimeStateAnchorMount {
return Err(StateAnchorError::OwnershipMismatch);
}
child_broker().reap(process)?;
let observed_work = open_directory(&authority.persistent_anchor, Path::new("work"))?;
require_same_descriptor(&observed_work, &authority.work)?;
clear_work_directory(&authority.work)?;
Ok(RuntimeStateAnchorAbsenceAuthority {
runtime_anchor: authority.runtime_anchor,
mount,

View file

@ -151,6 +151,7 @@ impl<'layout, 'configuration> FuseOverlayfsMount<'layout, 'configuration> {
}
self.layout.require_unmounted()?;
child_broker().reap(self.process)?;
self.layout.normalize_inactive_work_directory()?;
Ok(())
}
@ -165,6 +166,7 @@ impl<'layout, 'configuration> FuseOverlayfsMount<'layout, 'configuration> {
}
self.layout.require_unmounted()?;
child_broker().reap(self.process)?;
self.layout.normalize_inactive_work_directory()?;
Ok(())
}
}

View file

@ -6,6 +6,8 @@ use super::{
mount_identity, openat2, renameat, renameat_with, statat, unlinkat,
};
const FUSE_OVERLAYFS_NESTED_WORK: &str = "work";
/// Independently verifies that no mount is attached at the exact merged path.
///
/// # Errors
@ -240,18 +242,34 @@ pub(super) fn require_maintenance_work_shape(directory: impl AsFd) -> Result<(),
if names.is_empty() {
return Ok(());
}
if names.as_slice() != [OsString::from("work")] {
if names.as_slice() != [OsString::from(FUSE_OVERLAYFS_NESTED_WORK)] {
return Err(StateAnchorError::InvalidLayout(
"State Anchor work directory has unsupported entries",
));
}
let nested = open_directory(&directory, Path::new("work"))?;
let nested = open_directory(&directory, Path::new(FUSE_OVERLAYFS_NESTED_WORK))?;
require_nested_work_authority(&directory, &nested)?;
if directory_has_entries(&nested)? {
return Err(StateAnchorError::InvalidLayout(
"State Anchor work directory has unsupported entries",
));
}
Ok(())
}
pub(super) fn clear_work_directory(directory: impl AsFd) -> Result<(), StateAnchorError> {
remove_directory_contents(directory)
}
fn require_nested_work_authority(
directory: impl AsFd,
nested: impl AsFd,
) -> Result<(), StateAnchorError> {
let parent = rustix::fs::fstat(&directory).map_err(io::Error::from)?;
let child = rustix::fs::fstat(&nested).map_err(io::Error::from)?;
if child.st_mode & 0o7777 != 0o700
|| child.st_uid != parent.st_uid
|| child.st_gid != parent.st_gid
|| directory_has_entries(&nested)?
{
return Err(StateAnchorError::InvalidLayout(
"State Anchor work directory has unsupported entries",