Skip to content

feat(freebsd): detect the filesystem of block rootfs images - #1140

Open
yug105 wants to merge 3 commits into
urunc-dev:mainfrom
yug105:freebsd-docs-rootfs
Open

yug105 wants to merge 3 commits into
urunc-dev:mainfrom
yug105:freebsd-docs-rootfs

Conversation

@yug105

@yug105 yug105 commented Oct 21, 2026

Copy link
Copy Markdown

Description

The FreeBSD docs described an init_path list and a /sbin/init fallback that urunc doesn't implement. This PR corrects the docs and fixes block rootfs images being treated as UFS, which caused ext2 images to fail during boot. It detects the filesystem from the superblock, supports UFS1/UFS2 and ext2/ext3/ext4, and uses mountinfo for devmapper. Unknown or unsupported filesystems now return an error instead of defaulting to UFS.

Related issues

Fixes #1108

How was this tested?

  • Unit tests: Covered UFS1/UFS2, ext2, and invalid or missing files.

  • Boot tests: Tested with QEMU and Firecracker. Ext2 now boots successfully, and UFS continues to work.

    Before the fix (upstream main):

    Trying to mount root from ufs:/dev/vtbd0 []...
    Failed, superblock has critical errors
    Mounting from ufs:/dev/vtbd0 failed with error 2.
    

    After the fix:

    Trying to mount root from ext2fs:/dev/vtbd0 []...
    
  • End-to-end tests: All four tools (ctr, nerdctl, crictl, docker) passed in default and libcontainer modes, with zero failures across 157 test runs.

  • Lint: No new issues; the two existing dupl findings also occur on upstream main.

  • Performance: Adds approximately 0.06 ms to warm and 0.5 ms to cold container creation; startup time is unaffected.

Lab runs: [Full e2e suite](https://github.com/yug105/urunc-freebsd-lab/actions/runs/39581183764)
· [Filesystem and boot tests](https://github.com/yug105/urunc-freebsd-lab/actions/runs/39576957261)

LLM usage

Claude Opus 5.5 helped me understand the codebase.

Checklist

@netlify

netlify Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for urunc ready!

Name Link
🔨 Latest commit bced725
🔍 Latest deploy log https://app.netlify.com/projects/urunc/deploys/6aec60a01d3baa8d550fdb20
😎 Deploy Preview https://deploy-preview-1140--urunc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@cmainas cmainas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hello @yug105 , thank you for this. I have added some inline comments but some more generic:

  • The block detection should not take place in the create step but in start from the reexec process / monitor process, after pivot and namespaces joining.
  • Please us the correct filesystem type (e.g. ext2) and not just ext.

Comment thread docs/tutorials/freebsd-workloads.md Outdated

### Running without `urunit`

When the container command is non-empty, its first token is executed as init

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The container command can not be empty. Most tools will reject such an image.

Comment thread docs/tutorials/freebsd-workloads.md Outdated
exactly as written, with no `PATH` lookup. The path must not contain `:`,
because the FreeBSD kernel splits `init_path` on colons. The kernel's default
init search list (`/sbin/init:/sbin/oinit:/sbin/init.bak:/rescue/init`) applies
only when the command is empty.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

"the command is empty" - > "init is not specified".

Comment thread pkg/unikontainers/block.go Outdated
return "", err
}
if n == len(magic) && bytes.Equal(buf, magic) {
return "ufs", nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The fallback is a FreeBSD specific fiesystem which can not be recognized from nay other guest.

Comment thread pkg/unikontainers/block.go Outdated
} {
buf := make([]byte, len(magic))
n, err := f.ReadAt(buf, location+1372)
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Use the errors package to check the returned errors and not == or !=

Comment thread pkg/unikontainers/rootfs.go Outdated
if err != nil {
uniklog.Warnf("failed to detect filesystem type for explicit block %q: %v",
resolvedPath, err)
return result, true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The changes in the docs state "If the filesystem cannot be recognized or is unsupported, urunc reports an error.", but here we just log it.

Comment thread pkg/unikontainers/block.go Outdated

superblockLocations := []int64{8192, 65536, 262144, 0}

for _, location := range superblockLocations {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: This algorithm might report a wrong fs type. An ext4 block image setting UFS1 magic bytes at offset 66908 or 263516 is detected as ufs. Use a kernel implementation (e.g. from FreeBSD or Linux for the detection.

Comment thread pkg/unikontainers/block.go Outdated
return "", err
}
if n == 2 && bytes.Equal(buf, []byte{0x53, 0xEF}) {
return "ext2", nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We fall back to ext2 even for filesystems that are ext3/ext4.

Comment thread pkg/unikontainers/rootfs_test.go Outdated
expectedFsType: "ext2",
},
{
name: "missing image",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This does not sound correct. A missing image should not be accepted.

Comment thread pkg/unikontainers/block_test.go Outdated
want: "ufs",
},
{
name: "UFS2 at 8192 (makefs layout)",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It seems there is a mismatch here between FreeBSD kernel and makefs. The FreeBSD kernel expects the UFS2 superblock at 65536. I do not think it is a good idea to add a test for a case that the kernel does not accept.

urunc always passed ufs in vfs.root.mountfrom for a block image inside
the container image, so an ext2 image failed to mount as root.

Detect the filesystem in FreeBSD's Init, which runs in the start step
after the pivot, for both the reexec and the monitor process. UFS follows
the superblock locations and a subset of the checks of the FreeBSD kernel.
ext2, ext3 and ext4 are told apart by their superblock features, as in
util-linux.

Only ufs and ext2 are accepted. A missing image, an unrecognized
filesystem or an unsupported one is reported as an error.

Fixes: urunc-dev#1108
Signed-off-by: yug <yugagarwal105@gmail.com>
The docs described an init path list and an /sbin/init fallback that urunc
does not implement. Explain that the first token of the container command
is the init, what the guest does not get without urunit, and how urunc
detects the filesystem of a block rootfs.

Fixes: urunc-dev#1108
Signed-off-by: yug <yugagarwal105@gmail.com>
Signed-off-by: yug <yugagarwal105@gmail.com>
@yug105
yug105 force-pushed the freebsd-docs-rootfs branch from 31ac257 to bced725 Compare November 5, 2026 10:55
@yug105

yug105 commented Nov 5, 2026

Copy link
Copy Markdown
Author

Changes based on your review:

  • Moved filesystem detection from the create step to FreeBSD.Init, during start, after the pivot.
  • Reverted the shared code (block.go, rootfs.go, types.go) to upstream.
  • Added separate detection for ext2, ext3 and ext4 using their feature flags.
  • Switched to errors.Is for error handling.
  • Errors are now returned instead of just logged.
  • A missing image now results in an error.
  • An ext4 image with UFS1 magic at offsets 66908 or 263516 is correctly identified as ext4.
  • Incorporated both of your documentation wording suggestions.

A few things I'd like your guidance on:

  1. Detection location: Is FreeBSD.Init the right place for filesystem detection?
  2. ext2/ext3/ext4 support: Currently, only UFS and ext2 are accepted, although ext3 and ext4 also booted successfully in my QEMU tests. Would you prefer to keep rejecting ext3/ext4 or accept them as well?
  3. UFS validation: I'm currently implementing the superblock location rules along with a subset of the checks from validate_sblock. Is this sufficient, or would you prefer more comprehensive validation?

The UFS2-at-8192 test currently verifies that the image is rejected. Let me know if you'd prefer a different expectation or want me to remove this test.

Thanks again for the review. I'd appreciate your thoughts on these points before I finalize the implementation.

This branch has not been deployed

No deployments
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.

FreeBSD: docs update and rootfs detection

2 participants