NextBSD · Research · Alternate path
An alternate research path to the ZFS/SMB stack: adopt HFS+ as NextBSD's Mac-native filesystem — the authentic backing for a future AFP server and the old-Mac aesthetic — by finishing the freebsd_hfs port of Apple's fully-open HFS source into nextbsd-kernel, with the matching Apple utilities in nextbsd-userland.
.dmg/Time Machine to migrate Mac data into NextBSD — and likely moot once UFS is patched. It is not a default or alternate root filesystem. (ZFS is also not dropped — it just won't be the live-ISO filesystem; it remains a supported installed-system option.) The detailed port research below is retained as reference for the read-only-interop case; ignore the "alternate main path / replace UFS" framing.Viable, and cleaner than it first looks — because the whole HFS+ stack is already open. Every piece (kernel filesystem and tools) is APSL-2.0 in one Apple repo, so the work isn't inventing anything — it's finishing the port of code that already exists. But it's a real, staged effort with a hard core (rw + journaling correctness) and two NextBSD-specific sharp edges.
newfs_hfs/fsck_hfs/mount_hfs/hfs.util — is APSL 2.0 in apple-oss-distributions/hfs (hfs-715.120.4).freebsd_hfs is a read-mostly PoC (read works; write nascent; missing journaling, fsck, forks/ACLs/xattrs/case) — but all those gaps exist in Apple's source and "just" need porting to FreeBSD-15 VFS.vfs.pivot precedent: source into src-overlay/sys/fs/hfs/ + an options HFS config line — no fork patch. Utilities are a pure /sbin addition (FreeBSD has no HFS tools to conflict with). mount(8) stays FreeBSD; you just add mount_hfs.NO_MODULES → HFS+ compiled into the kernel, where its Darwin shim typedefs may collide statically with the existing Mach/Apple compat layer; (2) the FreeBSD 14→15 rebase (modest if the PoC truly crossed the 14 namei cleanup); (3) rw corruption risk — the intrinsic hard part.This is Path 2 of two parallel research directions; nothing here retracts Path 1.
| Layer | Path 1 — main (documented) | Path 2 — this doc alternate |
|---|---|---|
| Filesystem | ZFS | HFS+ (Mac-native) |
| File sharing | illumos smbsrv (SMB) | own AFP server (HFS+ native forks) |
| Directory / login | directoryservicesd — shared by both; independent of the FS choice | |
They diverge only at the filesystem + sharing layer and converge on the directory. The proposed sequencing for this track: HFS+ → own AFP server → DirectoryServices, with DirectoryServices run as a parallel track (it doesn't depend on HFS+/AFP), and HFS+ kept off the boot path (UFS root) until it's corruption-clean.
apple-oss-distributions/hfs (latest hfs-715.120.4, June 2026). Nothing is closed or absent. (xnu no longer carries bsd/hfs — it was split into this standalone repo; diskdev_cmds no longer ships the HFS tools either. The hfs repo is now canonical for everything.)| Component | In apple-oss-distributions/hfs | Feature-complete? |
|---|---|---|
| Kernel filesystem | core/ — journaling (hfs_journal.c), catalog + extents B-trees, resource/named forks, xattrs + ACLs (hfs_xattr.c), HFSX case-sensitive + case-insensitive, per-file encryption | ✅ full |
newfs_hfs (mkfs) | newfs_hfs/ | ✅ |
fsck_hfs + lib_fsck_hfs | fsck_hfs/ | ✅ (self-contained B-tree checker, no CoreFoundation) |
mount_hfs | mount_hfs/ | ✅ |
hfs.util (diskarb/mount helper) | hfs_util/ | ✅ (CoreFoundation-dependent) |
| Extras | CopyHFSMeta, fstyp_hfs, livefiles_hfs_plugin, … | ✅ |
The strategic consequence: the "missing" features in freebsd_hfs aren't gaps to invent — they're Apple code not yet ported. That turns the effort from "write a filesystem" into "finish a port," which is materially less risky (the on-disk-format and algorithm correctness already exist and are battle-tested).
freebsd_hfsgithub.com/stupendoussuperpowers/freebsd_hfs — a direct port of Apple's hfs/core to FreeBSD 14
| State | |
|---|---|
| Works | Read path: ACCESS/OPEN/CLOSE/READDIR/READ/READLINK/LOOKUP; ls, symlinks, ./.. correct. Write path started: VOP_CREATE (mkdir/touch), VOP_WRITE. |
| Missing (the port-to-finish) | journaling (TODO — rw is corruption-prone without it) · newfs_hfs/fsck_hfs (stubs) · mmap (EINVAL) · xattrs / resource forks / ACLs · case-insensitive vs HFSX handling · seek/truncate untested · macOS compat shims still present |
| Provenance | Ports Apple's core/hfscommon tree → APSL 2.0 by inheritance. Clean derivation, but the repo declares no top-level license — add an explicit APSL-2.0 LICENSE/attribution before building on it. |
| Activity | Single-author PoC; last commit 2025-07-02 ("Write support start"); "builds on work previously done on FreeBSD 5." |
So it's roughly Apple's read path + a nascent write path, minus journaling, tooling, and advanced metadata — a real head start on the mechanical VFS glue, with the hard correctness work (journaling, rw) still ahead. All of it is a porting job against Apple's complete source, not new design.
nextbsd-kernelnextbsd-kernel is not a fork of freebsd-src — it's a thin overlay applied over a baked /usr/src (releng/15.1): patches/*.patch (edits to freebsd files) + config/NEXTBSD (kernel config) + src-overlay/ (NextBSD-original sources copied in, with conf/files.* fragments appended). The custom vfs_pivot.c is the exact precedent for landing a new static subsystem — and HFS+ follows it almost verbatim.
# thousands of NEW files → src-overlay, NOT patches/
src-overlay/sys/fs/hfs/** # driver sources + private/format headers
src-overlay/conf/files.hfs # the sys/conf/files fragment:
fs/hfs/hfs_vfsops.c optional hfs
fs/hfs/hfs_vnops.c optional hfs
fs/hfs/hfs_journal.c optional hfs
... optional hfs
config/NEXTBSD # add: options HFS
.github/workflows/build.yml # new "Wire in HFS" step (clone of the vfs_pivot step):
cat src-overlay/conf/files.hfs >> /usr/src/sys/conf/files
printf 'HFS\topt_hfs.h\n' >> /usr/src/sys/conf/options
Registration is standard static-FS: VFS_SET(hfs_vfsops, hfs, 0) → vfs_register at SYSINIT, mount -t hfs → vfs_donmount → VFS_MOUNT. No loader, no kldload.
NO_MODULES=yes and the image assembler rm -f /boot/kernel/*.ko — so HFS+ must be compiled in (like ZFS/geom_uzip/unionfs already are). As a .ko, freebsd_hfs's Darwin compat shim resolved its symbols privately; compiled statically, its Darwin vocabulary (vnode_t, mount_t, proc_t, uio_t, kauth_cred_t…) shares one namespace with the Mach/Apple compat layer already in this kernel (sys/apple/, compat/mach). Duplicate typedefs → redefinition errors, or worse, silently divergent struct layouts. The single most likely first build-break. Fix (modeled on the repo's COMPAT_MACH_C pattern): a scoped HFS_C compile line, and — critically — make HFS consume the repo's existing Apple typedef headers rather than its own, so there's exactly one vnode_t.freebsd_hfs targets 14; NextBSD is 15.1. The expensive VFS churn (the namei/componentname/thread cleanup, NDFREE→NDFREE_PNBUF) landed in 14, not 15 — so if the PoC genuinely crossed that, 14→15 is days of -Werror fixups (renamed struct fields, new-ish VOPs defaulting to vop_eopnotsupp, a vget/insmntque signature). UNVERIFIED: if it was actually written against 13.x with private shims, this balloons into a real port. Confirm the PoC's true baseline early.A push touching src-overlay/**/config/**/build.yml triggers a kernel rebuild + a qemu smoke-test that boots the image to login:. So "PR the driver, iterate until it works" genuinely works — each push rebuilds with HFS compiled in and boots it.
fsx/fsstress CI job must be added before "it works" means anything for a read-write filesystem.nextbsd-userlandThe HFS tools are HFS-specific and FreeBSD base has none, so they belong in nextbsd-userland (the Apple system layer) as a pure addition — they supersede nothing. One bsd.prog.mk PROG per dir under src/hfs/, installed to /sbin (the mount/fsck helper path), not /usr/sbin.
| Tool | Action | Installs | Note |
|---|---|---|---|
newfs_hfs | Vendor | /sbin/newfs_hfs | touches raw device + hfs_format.h; no kernel ABI, no CF |
fsck_hfs | Vendor | /sbin/fsck_hfs | largest tool; self-contained checker; the recovery path (needed before rw is safe) |
mount_hfs | Vendor or ~150-line rewrite | /sbin/mount_hfs | the one fork — see below |
hfs.util | Defer | (.fs bundle) | DiskArbitration auto-mount hook; CF-dependent; add when diskarbitrationd reaches iter 5 |
mount(8) itself — keep FreeBSD's mount(8) verbatim. It already resolves -t hfs by exec'ing /sbin/mount_hfs. So you just add Apple's mount_hfs as that per-fstype helper (plus newfs_hfs/fsck_hfs), and mount -t hfs, fsck -t hfs, and fstab all work with zero changes to the mount framework.The one branch: Apple's mount_hfs builds a struct hfs_mount_args and calls mount(2). It vendors verbatim if the freebsd_hfs kernel kept Apple's hfs_mount_args ABI; if the driver went FreeBSD-native nmount(2) instead, mount_hfs.c is a ~150-line NextBSD-original rewrite. This is the key kernel↔userland handshake to confirm. Top shim risk: <sys/attr.h>/getattrlist (used by fsck/newfs) isn't in the tree yet — one write-new shim. Format headers should be sourced from the kernel driver's exported headers (single source of truth).
Land the overlay + options HFS; the boot smoke-test proves the kernel still boots with HFS linked in. Shakes out the static-link symbol collisions (#1) and the 14→15 -Werror fixups (#2). No mounting yet.
Gate MNT_RDONLY; mount a macOS-made HFS+ image in a VM; verify catalog/extent traversal, ls -R, checksummed reads. Zero corruption surface. This is where case-folding (HFS+ case-insensitive vs HFSX) first bites — get it right here.
Enable rw + journaling for data volumes / shares / removable media — never root. Highest corruption risk. Harness: fsx (data-integrity), fsstress (parallel metadata), mount/umount/fsck cycles, power-fail journal-consistency. Add the mount+fsx CI job here.
Port the remaining Apple code: resource forks, ACLs (→ FreeBSD VOP_[GS]ETACL), xattrs (→ VOP_[GS]ETEXTATTR), mmap. Each is an independent VOP-surface addition, testable in isolation. This is the layer that makes HFS+ worth it for AFP serving.
Only after C+D are corruption-clean. Requires changing the baked ROOTDEVNAME/vfs.root.mountfrom and — critically — surviving vfs.pivot's root-swap (mountcheckdirs() refcount balancing, union-layer membership). Keep HFS+ off the root path until then; UFS stays root, HFS+ serves data.
| Risk | Assessment |
|---|---|
| RW corruption (highest) | A PoC-grade rw+journaling driver on real data is the dominant risk; journal-replay + crash-consistency is where HFS+ ports historically corrupt. Mitigate with read-only-first, rw-non-root-only, and mandatory fsx/fsstress+crash-injection in CI. |
| 14→15 is a real port, not a rebase | UNVERIFIED — hinges on whether freebsd_hfs truly compiled against FreeBSD-14 VFS (post-namei cleanup). If 13.x-based, the estimate balloons. Confirm first. |
| Static-link symbol conflicts | Darwin typedefs in the HFS shim vs the Mach/Apple compat layer already in this NO_MODULES kernel — most likely first build-break. Fix: scoped HFS_C flags + reuse the repo's Apple typedef headers. |
mount_hfs ABI | Vendor-verbatim vs ~150-line rewrite depends on whether the kernel kept Apple's hfs_mount_args or went nmount(). Confirm at the kernel↔userland boundary. |
vfs.pivot if HFS+ ever roots | Stage E only; HFS+ would have to survive the union/root-swap refcount balancing. Kept off the root path until corruption-clean. |
| CI blind spot | Smoke-test gates on login: only; add a mount+fsx job or "green" is meaningless for a filesystem. |
| License hygiene | freebsd_hfs declares no repo license — add explicit APSL-2.0 before building on it. |
vfs.pivot + COMPAT_MACH patterns), the utilities are a clean additive /sbin drop, the CI already builds-and-boots on every push, and the whole thing is de-riskable (read-only → rw-data → features; UFS stays root). And it's the authentic backing for a native AFP server — exactly the old-Mac stack this project exists to revive.The longer-range vision this research serves: make HFS+ the filesystem, not just an add-on — and let it pull the userland toward Apple's
The staged roadmap above (§6) deliberately keeps UFS as root, HFS+ for data — the safe de-risking. The longer-range goal is more ambitious: HFS+ replaces UFS entirely, its Apple tools land in nextbsd-userland, the UFS equivalents are dropped from nextbsd-freebsd-compat, and — once the filesystem has moved — the FreeBSD POSIX userland is progressively swapped for Apple's system_cmds/file_cmds. This is the "become an authentic old Mac" direction, and it has a real technical logic, not just an aesthetic one.
cp/ls/tar don't understand any of it — they silently drop forks and mangle metadata. Apple's file_cmds are the tools that handle it correctly. So on UFS, swapping FreeBSD tools for Apple's buys nothing (which is exactly why NextBSD's current design uses FreeBSD's); on HFS+, Apple's tools finally have a reason to exist. HFS+-first is therefore the correct order — the FS choice justifies the userland swap.1. HFS+ as a DATA filesystem ───────── de-risk the driver (read-only → rw non-root) [§6 A–C]
2. HFS+ rw corruption-clean ────────── fsx/fsstress/crash-injection all green [§6 C]
3. Boot chain: small boot partition + HFS+ root ── rework livecd/vfs.pivot for HFS+ root ← GATE
4. HFS+ boots reliably as root ─────── NOW retire UFS + UFS tools from compat
5. Port Apple's HFS-metadata-aware file tools ── cp/ls/tar that handle forks (real reason)
6. (Optional) broader system_cmds swap ── purity, tool-by-tool, where it buys something
/boot pattern: a small UFS or FAT/EFI boot partition holds loader + kernel; the loader loads the kernel from there; the kernel (HFS+ compiled in) mounts the HFS+ root — no HFS+-in-loader needed. What that does not avoid is reworking the livecd + vfs.pivot flow (today deeply UFS/unionfs-based) for an HFS+ root. This is the gate that decides whether HFS+-as-root is real, and it needs its own scoping.Step 5 — the HFS+-metadata-aware tools (cp/ls/tar/archive) — is the part with a genuine capability reason and should be the real target. Step 6 — replacing the rest of the generic POSIX userland with Apple's — is mostly behavior-parity, not capability (Apple's build is functionally equivalent to FreeBSD's for generic tools), so it's a lot of shim work for authenticity alone. Do it gradually, tool-by-tool where it buys something, rather than as one big-bang replacement — unless authenticity itself is the goal, in which case it's a deliberate (large) cost.
Bottom line: the direction is coherent and on-brand — HFS+ root + Apple userland is literally "become an old Mac," and the FS→userland pull is a real justification. The cost is a multi-year program; the boot chain (step 3) is the gating unknown; and the governing rule is that UFS never leaves until HFS+ boots. The data-filesystem work (§6, steps 1–2) proves the driver before any of the riskier swaps begin.