Squashfs in the FreeBSD kernel — what exists, what's missing, what to actually build.
squashfuse exists as a port but it's FUSE, so it can't be your boot root without significant scaffolding./boot/modules, and ship a port — exactly the nvidia model. The KBI for VFS modules is in installed headers (VFS_SET, vnode ops, etc.).mkuzip(8) + geom_uzip(4): block-compressed read-only image with random access, supports zstd. It's the FreeBSD-native squashfs analog and it's already in the tree.gunion(8) (block-level, FreeBSD 14+) or unionfs-fuse. Avoid in-kernel unionfs — its man page literally says "IT DOESN'T WORK" and NomadBSD ripped it out for deadlocks.reboot -r is FreeBSD's switch_root — pivot from a tiny kernel+ramdisk into the real overlay-mounted root. This is exactly the Linux livecd pattern and it works on FreeBSD today.mkuzip + gunion + reboot -r gets you there with zero kernel work.A grep for squashfs across sys/, sbin/, usr.sbin/, contrib/ in the current source tree returns zero hits. There is no module skeleton, no GEOM provider, no VFS implementation. There is no mount_squashfs mount helper, and makefs(8) doesn't know about the format.
gsoc/squashfsvop_lookup / vop_readdir (directories), vop_read / vop_strategy (files), vop_readlink (symlinks). Compression: zlib, lzo2, zstd. Targets FreeBSD 13.2-RELEASE.main, fixing whatever's broken under the new VFS, and adding tests. Estimate: weeks of focused work, not days.squashfuse + squashfuse_ll, depends on FUSE 3, lz4, lzo2, zstd.squashfuse, then pivot — a lot more moving parts than just using a uzip image.mkuzip instead of squashfs recommendedBlock-compressed read-only image (zlib/lzma/zstd) with random-access TOC. Mounted via geom_uzip(4); you put a UFS on top.
Pros: in-tree, well-tested, used by every FreeBSD live distro, no kernel work needed, fast.
Cons: not squashfs (no Linux interop), no built-in dedup, slightly worse ratios on metadata-heavy trees.
Closest thing to squashfs that already works.
Revive the GSoC 2023 branch, package as a port that installs to /boot/modules/squashfs.ko, ship a mount_squashfs binary.
Pros: real squashfs, Linux interop, image portability.
Cons: weeks of kernel work, you become the maintainer, KBI churn at every major release.
Boot a minimal mfs_root with userland, run squashfuse to mount the image, reboot -r into it.
Pros: uses existing port, no kernel code.
Cons: FUSE in the boot path is fragile; performance hit; more moving parts than (A).
Recently merged in-tree (sys/fs/tarfs/): mount a zstd-compressed tar archive read-only as a filesystem. Originally built for FreeBSD container images.
Pros: in-tree, modern, zstd, simpler than uzip+ufs (single archive).
Cons: less battle-tested for live media, no random-access dedup like squashfs, hardlinks/xattr support varies.
FreeBSD's loader searches both /boot/kernel and /boot/modules by default — see sys/kern/kern_linker.c and stand/common/module.c:
static char linker_path[MAXPATHLEN] = "/boot/kernel;/boot/modules";
TUNABLE_STR("module_path", linker_path, sizeof(linker_path));
/boot/modules is the conventional drop-zone for third-party modules — it's exactly what nvidia, vendor wifi drivers, and various ports use.
The macro VFS_SET in sys/sys/mount.h is the registration hook, and it's part of the public KBI shipped in /usr/include/sys/. Every in-tree FS uses the same pattern; cd9660, fusefs, autofs, ext2fs, tarfs all build as both static and loadable.
VFS_SET(squashfs_vfsops, squashfs, VFCF_READONLY);
MODULE_VERSION(squashfs, 1);
An out-of-tree port-ready Makefile looks like this, leveraging /usr/share/mk/bsd.kmod.mk:
# Makefile in your repo
KMOD= squashfs
SRCS= squashfs_vfsops.c squashfs_vnops.c squashfs_io.c \
squashfs_decompress.c \
vnode_if.h opt_kstack_pages.h
KMODDIR= /boot/modules # default is /boot/kernel; set to modules
.include <bsd.kmod.mk>
Build it against installed headers (no source tree required) by setting KERNBUILDDIR=/usr/include/sys. The ports framework (USES=kmod) handles all of this for you.
FreeBSD's KBI is stable within a STABLE branch (e.g. 14.x), but not across major versions. You'd ship one binary per major release, or a source port that rebuilds locally. This is the standard third-party module trade-off.
Out-of-tree is the right answer for an experiment, a third-party project, or a "live distro toolkit". You only need to upstream into sys/fs/squashfs if you want it loaded by the loader as part of a release build, or if you want it in /usr/include/sys for other consumers. Even then, you can develop entirely as a port and merge later.
Each in-kernel FS has a corresponding sbin/mount_* helper that just calls nmount(2) with the right fstype. Look at sbin/mount_cd9660/ as the template — it's ~250 lines. Your mount_squashfs would be similar.
rescue/rescue/Makefile already statically links mount_cd9660, mount_msdosfs, mount_nullfs, mount_unionfs, mount_udf, mount_nfs. Adding mount_squashfs is one line, but only matters if squashfs is in tree. For an out-of-tree port, ship the binary in /usr/local/sbin/.
Already packaged: sysutils/squashfs-tools. You don't have to build these yourself — your live-ISO build script depends on the port.
If you want makefs -t squashfs to work natively (so the release build can produce squashfs images without external tools), add a backend in usr.sbin/makefs/ alongside cd9660.c. This is purely cosmetic — mksquashfs from the port already works.
The release ISO (release/amd64/mkisoimages.sh) is a plain cd9660 image with /etc/fstab set to mount root ro. rc.initdiskless creates tmpfs overlays for /etc, /var, /tmp from cpio templates in /conf/. It works, but it's not compressed and the "writable" parts are limited to a few directories. There's no whole-rootfs writability.
| Layer | Linux | FreeBSD equivalent |
|---|---|---|
| Compressed read-only base | squashfs (zstd) | mkuzip on UFS, OR squashfs (out-of-tree) |
| Writable overlay | OverlayFS (file-level) | gunion(8) (block-level, 14.0+) — or unionfs-fuse |
| Writable backing | tmpfs upper | swap-backed md(4) via mdconfig -t swap |
| Pivot | switch_root from initramfs | reboot -r (since FreeBSD 11) |
| Boot media FS | cd9660 + initramfs | cd9660 (loader can't read uzip directly; ramdisk image lives inside cd9660) |
mfs_root image (small UFS, ~5–10 MB)./. Init runs a tiny /sbin/init shell script.mdconfig -t vnode -f /cdrom/rootfs.uzip, mounts the resulting /dev/mdN.uzip read-only at /newroot.mdconfig -t swap -s 2g), then either
gunion create md0.uzip md1 → put a UFS on the resulting union device, mount at /newroot; ortmpfs on /newroot.upper, mount unionfs-fuse with lower=uzip, upper=tmpfs.vfs.root.mountfrom to the new device, reboot -r./sbin/init from the live system. You're now running with a fully writable rootfs.Frommount_unionfs(8)in the current tree:
"THIS FILE SYSTEM TYPE IS NOT YET FULLY SUPPORTED (READ: IT DOESN'T WORK) AND USING IT MAY, IN FACT, DESTROY DATA ON YOUR SYSTEM."
Don't use it. NomadBSD switched to unionfs-fuse after deadlocks during reboot -r. Your two real overlay choices are gunion(8) (block-level, in-tree as of 14) or unionfs-fuse (file-level, port).
| Distro | Read-only base | Writable layer | Pivot |
|---|---|---|---|
| helloSystem | mkuzip (UFS inside) | Copy into swap-backed memdisk (no overlay; just duplicates) | reboot -r |
| NomadBSD | mkuzip | unionfs-fuse | direct mount |
| FuryBSD | mkuzip | same as helloSystem | reboot -r |
| GhostBSD | cd9660 | full RAM copy (4 GB+) | n/a |
| mfsBSD | tar.gz into MFS | everything in RAM | n/a |
Nobody uses squashfs. Every active project uses mkuzip.
reboot -r (reroot)The FreeBSD analog of Linux's switch_root. Implemented in sbin/init/init.c (the userland half) and sys/kern/kern_shutdown.c (kern_reroot()). Restricted to PID 1.
/dev/reroot and re-execs from there.reboot(RB_REROOT)./dev, calls vfs_mountroot() with the new vfs.root.mountfrom./sbin/init from the new root.reboot -r.Don't write a kernel module. Build with what's already in the tree:
makefs -t ffs).mkuzip -j16 -d -s 65536 rootfs.ufs (zstd, 64K blocks). Expect ~40-50% of original size for a typical install./sbin/init, mdconfig, mount, gunion, your pivot script, and a bare /dev.makefs -t cd9660 -o rockridge,bootimage=....gunion with swap-backed upper → put UFS on union → reboot -r.Reference implementations to read first: helloSystem ISO build, NomadBSD build script.
squashfs/{Makefile, squashfs_vfsops.c, squashfs_vnops.c, ...}.mount_squashfs userland binary under squashfs/mount_squashfs/.filesystems/squashfs-kmod + USES=kmod) that builds against installed headers and installs to /boot/modules/squashfs.ko.mkuzip+mdconfig with mksquashfs+mount_squashfs.You can do this entirely outside the FreeBSD tree. Upstream later if it's healthy.
Use fusefs-squashfuse from ports as the read-only layer. Boot scaffolding looks like (1) tiny mfs_root with fusefs.ko preloaded, squashfuse in /sbin, (2) script attaches the squashfs image, squashfuse rootfs.sqsh /newroot, (3) gunion-style overlay on top (note: gunion is block-level so it sits under, not above, FUSE — for FUSE you'd need unionfs-fuse instead), (4) reboot -r. Slower than uzip and an extra moving part, but no kernel code.
| Component | Path (if in tree) or repo (if out) | Notes |
|---|---|---|
| kernel module source | sys/fs/squashfs/ or your-repo/squashfs/ | Out-of-tree fine |
| module Makefile | sys/modules/squashfs/ or repo top-level | KMODDIR=/boot/modules |
| VFS registration | in your squashfs_vfsops.c | VFS_SET(... , VFCF_READONLY) |
mount_squashfs | sbin/mount_squashfs/ or port | Model on sbin/mount_cd9660/ |
| mksquashfs | not yours | Already in sysutils/squashfs-tools |
| rescue inclusion | rescue/rescue/Makefile | Only matters if upstreamed |
makefs -t squashfs | usr.sbin/makefs/squashfs.c | Optional polish |
| release/livefs build | release/amd64/mkisoimages.sh + your build script | Most realistic to keep entirely out-of-tree as a separate "freebsd-livecd" project repo |
| boot loader squashfs reader | stand/libsa/ | Only needed if you want to boot directly from squashfs without an mfs_root shim. Significant work — skip it. |
VFS_SET macro definition.MD_ROOT mfs_root mechanism (lines 173-198, 2057-2062).kern_reroot() implementation.reboot -r.KERNBUILDDIR.defaults.sh — read-only-root reference (root_rw_mount="NO").Generated 2026-05-04. Research synthesized from a deep search of /Users/jmaloney/freebsd-src (current main, last commit 045a9ef829fa) plus external research across FreeBSD status reports, GSoC archives, and the active FreeBSD-derived live-distro projects.