9.1 KiB
Phase 1.3: FreeBSD system call interface mapping for Guix and Shepherd porting
Date: 2026-04-01
Summary
This step completes the Phase 1.3 deliverable by documenting how the FreeBSD system interface maps to the Linux-oriented assumptions visible in current Guix source code, and by adding a runnable C harness that exercises the most important FreeBSD-side primitives.
Added files:
tests/system/freebsd-syscall-mapping.ctests/system/run-freebsd-syscall-mapping.sh
Results from the runnable harness show that the current FreeBSD host provides and successfully exercises:
fork/waitpidposix_spawn_file_actions_addclosefrom_npclose_rangelutimesstatvfschroot(root test)jail(2)(root test)lchown(root test)
The same harness also confirms several key Linux namespace-oriented interfaces are absent on this host:
cloneunsharesetnspivot_rootsys/prctl.h
Additionally, posix_fallocate is present but returned EOPNOTSUPP on the tested filesystems, which is a notable semantic difference from a simple configure-time link check.
Sources inspected
Guix / Nix daemon source paths
The mapping work was based on current Guix source inspection, especially:
~/repos/guix/nix/libstore/build.cc~/repos/guix/configure.ac
Relevant Linux-oriented mechanisms visibly referenced there include:
- chroot-based build roots
- build users via
setuid/setgid - Linux namespaces through
clone/unshare/setns pivot_root- seccomp and
prctl - mount namespace behavior and bind mounts
- filesystem metadata calls such as
lchown - store space checks through
statvfs
FreeBSD interface/man-page references
The mapping also used current FreeBSD interfaces/documentation from the host, including:
jail(2)chroot(2)closefrom(2)/close_range(2)mount(2)/nmount(2)mount_nullfs(8)cap_enter(2)cap_rights_limit(2)lutimes(2)lchown(2)posix_fallocate(2)pdfork(2)
Runnable validation harness
The new harness can be run with:
METADATA_OUT=/tmp/freebsd-syscall-mapping-metadata.txt \
./tests/system/run-freebsd-syscall-mapping.sh
Observed output on the current host:
feature.SYS_clone=no
feature.SYS_unshare=no
feature.SYS_setns=no
feature.SYS_pivot_root=no
feature.FreeBSD_jail=yes
feature.Capsicum_headers=yes
feature.sys_prctl_h=no
feature.linux_close_range_h=no
runtime.fork_waitpid=ok
runtime.posix_spawn_addclosefrom_np=ok
runtime.close_range=ok
runtime.posix_fallocate=unsupported-on-tested-filesystem
runtime.lutimes=ok
runtime.statvfs=ok
root.chroot=ok
root.jail=ok
root.lchown=ok
Mapping by functional area
1. Process creation and supervision
| Guix/Linux expectation | FreeBSD status | Mapping / notes |
|---|---|---|
fork / waitpid |
Available and validated | Direct mapping works. |
posix_spawn helpers |
Available and validated | Works once Guile uses the fixed local build; posix_spawn_file_actions_addclosefrom_np exists on FreeBSD. |
pdfork process descriptors |
FreeBSD-specific extra facility | Not currently used by Guix, but relevant as a possible future supervision/containment primitive. |
Assessment
FreeBSD is not blocked at the basic process-creation layer. The earlier Guile subprocess crash was an ABI mismatch in Guile/gnulib usage, not a lack of kernel support for subprocess creation.
2. File-descriptor cleanup and process hygiene
| Guix/Linux expectation | FreeBSD status | Mapping / notes |
|---|---|---|
close_range |
Available and validated | Directly present in FreeBSD libc. |
closefrom behavior |
Available | Also native on FreeBSD and useful for daemon/build-helper hygiene. |
posix_spawn_file_actions_addclosefrom_np |
Available and validated | Strongly relevant because Guile and Guix subprocess helpers rely on this class of operation. |
Assessment
FreeBSD provides good native support for descriptor-sweeping operations. This is a positive compatibility point rather than a gap.
3. Filesystem isolation and chroot-style build roots
| Guix/Linux expectation | FreeBSD status | Mapping / notes |
|---|---|---|
chroot build roots |
Available and validated | Directly usable; root-only as expected. |
| bind-mount-style exposure of declared inputs | No Linux bind mounts, but equivalent behavior exists | Use nullfs mounts plus ordinary mount orchestration rather than Linux bind mounts. |
pivot_root |
Absent | Must not be relied on; jail/chroot/nullfs-based setup is the practical replacement direction. |
Assessment
The core “restricted filesystem root containing only declared paths” idea is achievable on FreeBSD, but the implementation must be rethought around chroot, jail, mount/nmount, and nullfs, rather than Linux mount namespaces plus pivot_root.
4. Namespace-based isolation
| Guix/Linux expectation | FreeBSD status | Mapping / notes |
|---|---|---|
clone(CLONE_NEW*) |
Absent | No direct equivalent. |
unshare |
Absent | No direct equivalent. |
setns |
Absent | No direct equivalent. |
| user namespaces | Absent in Linux sense | Must be replaced with jail design and traditional privilege separation. |
| mount namespaces | Absent in Linux sense | Must be replaced with jail/chroot + mount arrangement. |
| network namespaces | Absent in Linux sense | Use VNET jails when network isolation is required. |
| PID namespaces | Absent in Linux sense | Jail process isolation is the closest available model. |
Assessment
This is the single largest architectural gap between current Guix daemon code and FreeBSD. It confirms the Phase 2 design direction: Guix daemon isolation on FreeBSD cannot be a syscall-for-syscall translation; it must be a jail-oriented redesign.
5. Mount and store exposure mechanics
| Guix/Linux expectation | FreeBSD status | Mapping / notes |
|---|---|---|
mount operations for build roots |
Available | FreeBSD provides mount(2) and nmount(2). |
| bind mounts | Different implementation model | nullfs is the practical analog for exposing existing paths elsewhere in the namespace. |
| recursive mount-namespace behavior | No direct equivalent | Must be handled explicitly in jail/chroot mount layout. |
Assessment
A FreeBSD Guix daemon will need explicit mount planning rather than namespace-based mount isolation. nullfs is the most natural replacement for the Linux bind-mount role in store/input exposure.
6. Privilege dropping and capability models
| Guix/Linux expectation | FreeBSD status | Mapping / notes |
|---|---|---|
setuid / setgid build users |
Available | Traditional Unix credential switching remains available. |
Linux capabilities (CAP_*) |
Absent | No direct equivalent. |
prctl-style Linux process controls |
Absent on this host | Must not be assumed. |
| seccomp filter model | Linux-specific | No direct equivalent. |
| Capsicum capability mode | Available on FreeBSD | Useful complementary mechanism, but not a 1:1 replacement for Linux capabilities or namespaces. |
Assessment
FreeBSD can still do classic build-user isolation, but the Linux capability/seccomp model must be replaced by a different combination of jails, traditional credentials, filesystem layout, and possibly Capsicum in carefully chosen places.
7. Metadata, timestamps, and storage primitives
| Guix/Linux expectation | FreeBSD status | Mapping / notes |
|---|---|---|
lchown |
Available and validated | Works in root test. |
lutimes |
Available and validated | Works. |
statvfs |
Available and validated | Works and is already referenced in current daemon code. |
statx |
Absent | Must use older stat/lstat/fstatat style interfaces instead. |
posix_fallocate |
Present but runtime-limited | Returned EOPNOTSUPP on the tested filesystems; presence does not imply useful semantics everywhere. |
Assessment
Most metadata operations map directly, but statx has no FreeBSD equivalent and posix_fallocate requires semantic caution rather than a simple availability check.
Porting implications for Phase 2
The system-call mapping work strongly supports the following Phase 2 design assumptions:
-
Use jails as the primary isolation model. Linux namespace code paths are not portable as-is.
-
Use
nullfs+chroot/jail layout instead of Linux bind mounts + mount namespaces. -
Retain build users and classic UID/GID switching. These mechanisms remain directly usable on FreeBSD.
-
Do not depend on Linux seccomp/capability/prctl machinery. Any comparable restrictions must come from a different design.
-
Treat
posix_fallocateconservatively. Configure-time presence is not enough; runtime filesystem behavior matters.
Conclusion
Phase 1.3 is now satisfied by:
- a concrete source-based mapping between Guix's Linux-oriented daemon assumptions and FreeBSD facilities
- a runnable C harness validating the most important FreeBSD-side primitives
- explicit identification of the irreducible architectural gap: Linux namespaces versus FreeBSD jails
This provides enough detail for Phase 2 work to proceed with a jail-first design instead of attempting a misleading syscall-by-syscall translation.