Skip to content

hostfs: FsBackend, mounts served by the embedder - #181

Merged
danbugs merged 2 commits into
mainfrom
fs-backend
Oct 21, 2026
Merged

danbugs merged 2 commits into
mainfrom
fs-backend

Conversation

@danbugs

@danbugs danbugs commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Adds Mount::backend, so an embedder can serve a guest mount itself instead of handing over a host directory. hyperlight-sandbox needs it to serve Unikraft guests' /input and /output through CapFs, the code behind its WASI file calls, so quotas, permissions and link rules are checked per operation rather than after a run (hyperlight-dev/hyperlight-sandbox#252).

  • FsBackend has one method per hostfs operation: stat, read, write, create_dir, remove, truncate, list, rename. symlink and hard_link refuse by default, and chmod is ignored by default. Errors reach the guest as errno, and a panic in a backend is caught and becomes EIO.
  • Host directories are the built-in backend, so Mount::rw and Mount::ro behave as before. A read-only mount still refuses writes with EROFS before its backend is asked.
  • FileStat and DirEntry are #[non_exhaustive], with constructors. The docs spell out what a backend is given: untrusted paths and offsets, list called once per entry and expected to keep a stable order, and files and directories only, since the kernel makes no symlink nodes on hostfs.
  • Hardened: names a listing can't carry are dropped, readlink replies are capped, and a raw OS error of 0 or less becomes EIO.
  • Fix: os.chmod on any mount failed with EIO. The host took the mode as u32, but the kernel sends a C int. It now works, with only the permission bits passed on, so a guest can't set setuid, setgid or sticky on host files.
  • Breaking: Mount has a private field, so build one with its constructors and destructure it with ...
  • Tested with Python guests against an in-memory quota backend (quota, EROFS, huge offsets), against a backend that panics or returns bad answers, and with chmod on a directory mount.

@danbugs
danbugs force-pushed the fs-backend branch 2 times, most recently from b18f9bd to ceb7915 Compare October 21, 2026 22:17
@danbugs
danbugs changed the base branch from net-filter to main October 21, 2026 22:43
@danbugs danbugs closed this Oct 21, 2026
@danbugs danbugs reopened this Oct 21, 2026
@danbugs

danbugs commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

Folded into #179, which now carries both the network filter and these FsBackend commits.

@danbugs danbugs closed this Oct 21, 2026
@danbugs danbugs reopened this Oct 21, 2026
The kernel's hostfs_setattr passes the mode as a C int; the host
function took a u32, so hyperlight refused every call on its parameter
type and os.chmod on any mount failed with EIO.

Signed-off-by: danbugs <danilochiarlone@gmail.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Host-directory listings collect all entries before enforcing the response limit, allowing excessive host memory use and metadata work.

1 open issue
What changed in this PR

Adds embedder-served filesystem mounts so applications can enforce quotas, permissions, and path policies per guest operation while retaining host-directory mounts.

Changes:

  • Introduces FsBackend, metadata types, and Mount::backend.
  • Routes hostfs operations through backends, adds response validation, and fixes chmod argument handling.
  • Adds integration tests and documents backend contracts and the breaking Mount construction change.
File Description
tests/​python.rs Tests host-side chmod behavior and permission masking.
tests/​fs_backend.rs Tests memory-backed mounts, quotas, read-only access, and invalid backend responses.
src/​main.rs Uses mount constructors in CLI parsing.
src/​lib.rs Exports backend APIs and adds backend mount construction.
src/​hostfs.rs Implements backend dispatch, panic handling, and response validation.
src/​errno.rs Maps nonpositive raw error codes to EIO.
src/​cli/​manifest.rs Updates mount path resolution for the private backend field.
docs/​fs.md Documents backend usage, responsibilities, and protocol limits.
CHANGELOG.md Records backend support, API changes, and the chmod fix.

Review effort: Balanced

Comment thread src/hostfs.rs Outdated
Comment on lines +251 to +261
// TODO: an entry that cannot be read is skipped and a non-UTF-8
// name is decoded lossily, neither reported to the guest.
Ok(self
.0
.read_dir(path)?
.flatten()
.map(|entry| DirEntry {
is_dir: entry.metadata().map(|m| m.is_dir()).unwrap_or(false),
name: entry.file_name().to_string_lossy().into_owned(),
})
.collect())

@danbugs danbugs Oct 21, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2566c9d: DirBackend::list now counts the serialized size as it goes and stops one entry past what a reply holds, so fs_list still answers EOVERFLOW without reading the rest of the directory into host memory. A unit test lists a 2,000-entry directory and checks it stops just past the budget; the Python EOVERFLOW guest test still passes.

hostfs's operations go through an FsBackend now, one method each, with
the wire encoding kept in hostfs. A host directory is the built-in
backend, so Mount::rw and Mount::ro behave as before; Mount::backend
mounts one of the embedder's instead, which decides every stat, read,
write, listing and rename the guest's VFS makes, and answers with an
io::Error the guest sees as errno. A read-only mount is still refused
writes before its backend is asked.

Signed-off-by: danbugs <danilochiarlone@gmail.com>
@danbugs
danbugs merged commit 47bc9e2 into main Oct 21, 2026
60 checks passed
@danbugs
danbugs deleted the fs-backend branch October 21, 2026 23:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants