From a71e7cea756e28a1a7da68b8d8bce10fc9df0b30 Mon Sep 17 00:00:00 2001 From: atlas Date: Fri, 6 Nov 2026 15:45:01 +0100 Subject: [PATCH] allow erofs as the idmapped store filesystem nspawn idmaps the store on erofs, which the VM test's store uses; the allowlist from the store check refused it and failed the namespaced-agent subtest in vm-nightly #7084. vm-nightly #7030 ran the same agent on the same erofs store green. The magic is EROFS_SUPER_MAGIC_V1 from the kernel's linux/magic.h; libc has no constant. Refs #5888 --- docs/agent-lifecycle/persistence.md | 11 ++++++----- hive-priv/src/userns.rs | 15 +++++++++------ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/agent-lifecycle/persistence.md b/docs/agent-lifecycle/persistence.md index a25d5b1d..57dfbc14 100644 --- a/docs/agent-lifecycle/persistence.md +++ b/docs/agent-lifecycle/persistence.md @@ -488,13 +488,14 @@ hive-c0re can't read, or none, leaves the agent as it is. user. hive-priv mounts the agent's `state/`, `harness/`, `work/` and `claude/` dirs and its socket dir with `owneridmap`, and every other bind too (config, `/knowledge`, the runtime dir), whose owner shows as root inside. -- **`/nix/store` has to be on ext4, btrfs or xfs.** nspawn mounts the store - into a namespaced container idmapped, and the kernel refuses that on +- **`/nix/store` has to be on ext4, btrfs, xfs or erofs.** nspawn mounts the + store into a namespaced container idmapped, and the kernel refuses that on overlayfs and on some other filesystems. `WriteNspawnFlags` for an opted-in agent checks the `statfs` type of `/nix/store` against an allowlist of those - three. The check refuses every other filesystem, zfs and tmpfs included, even - where a newer kernel could idmap it, because the gate exists to stop a - container that would not boot. The error names the type and the fix, and + four; the VM test's erofs store idmaps on the hive's kernel. The check + refuses every other filesystem, zfs and tmpfs included, even where a newer + kernel could idmap it, because the gate exists to stop a container that would + not boot. The error names the type and the fix, and comes before anything moves. Check a host with `findmnt -no FSTYPE /nix/store`. - **The container starts with `--private-users=pick --private-users-ownership=map`.** diff --git a/hive-priv/src/userns.rs b/hive-priv/src/userns.rs index e96c3a35..92f57ed4 100644 --- a/hive-priv/src/userns.rs +++ b/hive-priv/src/userns.rs @@ -51,8 +51,11 @@ const STORE: &str = "/nix/store"; type FsType = libc::__fsword_t; /// `f_type` of the filesystems that support idmapped mounts and that a store -/// can sit on: ext4, btrfs and xfs. Nothing else is accepted. -const IDMAP_FS: [FsType; 3] = [0xEF53, 0x9123_683E, 0x5846_5342]; +/// can sit on: ext4, btrfs, xfs and erofs (`EROFS_SUPER_MAGIC_V1` in the +/// kernel's `linux/magic.h`; `libc` has no constant for it). Nothing else is +/// accepted. A namespaced agent ran idmapped binds on the VM test's erofs +/// store, so erofs idmaps on our kernel. +const IDMAP_FS: [FsType; 4] = [0xEF53, 0x9123_683E, 0x5846_5342, 0xE0F5_E1E2]; /// The dirs under `/` the agent writes into. `config` /// is not one of them: hive-core writes it and the agent only reads it. `state` @@ -111,7 +114,7 @@ fn fs_type(path: &Path) -> std::io::Result { Ok(buf.f_type) } -/// Refuse a store whose filesystem is not ext4, btrfs or xfs. nspawn clones a +/// Refuse a store whose filesystem is not ext4, btrfs, xfs or erofs. nspawn clones a /// namespaced container's `/nix/store` bind with an idmap, which the kernel /// refuses on other filesystems, so the container would not boot. Others are /// refused even where a newer kernel could idmap them. @@ -122,7 +125,7 @@ fn check_store(agent: &str, f_type: std::io::Result) -> Result<()> { } bail!( "{agent} sets privateUsers, but {STORE} is on a filesystem (f_type {f_type:#x}) other \ - than ext4, btrfs or xfs, the only ones accepted; move {STORE} to one of them, or leave \ + than ext4, btrfs, xfs or erofs, the only ones accepted; move {STORE} to one of them, or leave \ privateUsers off for this agent" ) } @@ -686,14 +689,14 @@ mod tests { /// looks. #[test] fn the_store_has_to_be_on_a_filesystem_that_can_be_idmapped() { - for t in [0xEF53, 0x9123_683E, 0x5846_5342] { + for t in [0xEF53, 0x9123_683E, 0x5846_5342, 0xE0F5_E1E2] { check_store("a", Ok(t)).unwrap(); } // overlayfs and tmpfs for t in [0x794c_7630, 0x0102_1994] { let msg = format!("{:#}", check_store("a", Ok(t)).unwrap_err()); assert!( - msg.contains(&format!("{t:#x}")) && msg.contains("ext4, btrfs or xfs"), + msg.contains(&format!("{t:#x}")) && msg.contains("ext4, btrfs, xfs or erofs"), "{msg}" ); }