Repository navigation
capi: Check expected_fsverity_digest and fix lcfs_mount_image() - #5
Closed
cgwalters-bot wants to merge 5 commits into
Closed
cgwalters-bot wants to merge 5 commits into
cgwalters-bot wants to merge 5 commits into
Conversation
libcomposefs is built on its own (`make install-capi`, which the RPM spec's `make install` runs), so it never gets the kernel compat features cfsctl and composefs-setup-root enable by default. Without them, lcfs_mount_image() passes the detached EROFS mount to overlayfs by fd, which kernels before 6.15 reject with EBADF. ostree-prepare-root on CentOS Stream 10 (6.12) fails to mount the deployment's composefs image that way. The C library works on older kernels at runtime; give the Rust one the same features and defaults as the other binaries. Generated-by: AI
With object directories, lcfs_mount_fd() mounted the EROFS image and
then passed that mount to composefs_fsmount(), which expects the image
file and mounts it again. The second mount gets a directory as its
source and fails with ENOTBLK, so every composefs mount with a
basedir failed. ostree-prepare-root hits this on boot ("composefs:
failed to mount: Block device required") and drops to the emergency
shell.
Only mount the EROFS image ourselves when there's no overlay on top.
Generated-by: AI
lcfs_mount_fd() ignored expected_fsverity_digest and mounted whatever image it was given. The C library measures the image's fs-verity digest and refuses to mount it on a mismatch, and callers rely on that: ostree-prepare-root passes the digest of the deployment's composefs image, so with our library its verification silently didn't happen. Check it the way C does: the kernel's sha256 fs-verity measurement of the image must equal the expected digest, failing with EWRONGVERITY (EILSEQ) if it doesn't and ENOVERITY (ENOTTY) if the image has no fs-verity. Also validate the options up front as C does, so an unparseable digest, unknown flags, an upperdir without a workdir (or the reverse) and IDMAP without an fd fail with EINVAL before anything is opened or mounted. Generated-by: AI
lcfs_mount_fd() ignored upperdir and workdir, so a caller asking for a writable composefs (ostree does, for a transient root) got a read-only mount without its upper layer. Pass them to the overlayfs mount, and like C make the mount writable unless LCFS_MOUNT_FLAGS_READONLY is set. Generated-by: AI
Nothing tested mounting through the C API: the capi CI job runs the C test suite, which doesn't mount anything. So lcfs_mount_image() failed for every image with an object directory, and ignored expected_fsverity_digest, without any test noticing. The test image now also builds our libcomposefs and lcfs-mount-test, a small C program that mounts an image the way ostree-prepare-root does. The tests mount an image from a verity-enabled repository and read back an inline and an external file, with and without the correct digest; check the errors for a wrong digest, an unparseable one and an image without fs-verity; and check that an upper layer is writable, or read-only with LCFS_MOUNT_FLAGS_READONLY. Generated-by: AI
cgwalters-bot
force-pushed
the
bot/capi-mount-fixes
branch
from
September 25, 2026 15:07
c74a9c5 to
f1752b5
Compare
cgwalters
approved these changes
Sep 25, 2026
Collaborator
Author
|
Opened upstream as composefs#401. Closing this review draft. |
Collaborator
Author
|
Signed off 5 commit(s) with |
Collaborator
Author
|
Signed off 1 commit(s) with |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Security:
lcfs_mount_fd()never readexpected_fsverity_digest. The C library measures the image's fs-verity digest and refuses to mount on a mismatch (lcfs-mount.clcfs_validate_verity_fd). ostree-prepare-root passes that digest for the deployment's composefs image, so on the Rust library its verification silently didn't happen. This PR checks the digest as C does:EWRONGVERITY(EILSEQ) on a mismatch,ENOVERITY(ENOTTY) when the image has no fs-verity, andEINVALfor an unparseable digest. The other options are validated up front, as in C.It also fixes
lcfs_mount_image()/lcfs_mount_fd()with object directories, which failed for every image:pre-6.15compat code, and 6.12 kernels rejected the mount with EBADF. It now enables it by default, like cfsctl and composefs-setup-root.Also:
upperdir/workdirare now passed through (ostree uses them for a transient root), and the mount is writable unlessLCFS_MOUNT_FLAGS_READONLYis set, as in C.New privileged integration tests run
lcfs-mount-test, a small C program built into the test image against our libcomposefs. It mounts an image from a verity-enabled repository the way ostree-prepare-root does and reads back an inline and an external file, with and without the correct digest. The tests also cover wrong, unparseable and+-prefixed digests, an image without verity, and an upper layer that is writable, or read-only withLCFS_MOUNT_FLAGS_READONLY. An empty digest never matches, which is deliberately stricter than C (C skips the check).Tested on a RHEL 10 devspace (6.12):
just test-integration-vm capi --test-threads=1(CentOS Stream 10 VMs): 4/4 pass. The same tests as root on the host pass against the system C libcomposefs too, so the errors match C. On main, all 4 fail.cargo test -p composefs-capi,just test-capi, clippy-D warningsandcargo fmt --checkpass.Not done here, follow-ups:
image_mountdiris still ignored. On pre-6.15 kernels, the temporary EROFS mount goes into atempfiledirectory instead of the caller's path (ostree passes/run/ostree/.private/cfsroot-lower). Fixing that needs acomposefs::mount::MountOptionsfield threaded intoprepare_mount().n_objdirs == 0still mounts the bare EROFS image, where C returns EINVAL..fileredirect bug inconvert.rsis separate. The ostree test in bootc: Test ostree against the Rust libcomposefs #4 still fails on it.Related: composefs#323
Generated-by: https://github.com/cgwalters/#llms
Review draft in cgwalters-forge, not upstream yet. This section is removed when the PR is opened upstream.
composefs/composefs-rs, basemainPVTI_lAHOAQ_SPs4Bj2Gizg8wsH8To review:
/promoteon a line of its own, to open it upstream, ready for review. Either covers only the commits pushed so far./draftline (in the same comment or before) to open it upstream as a draft (/readyundoes that).