Files
fruix/docs/reports/phase1-freebsd-syscall-mapping.md

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.c
  • tests/system/run-freebsd-syscall-mapping.sh

Results from the runnable harness show that the current FreeBSD host provides and successfully exercises:

  • fork / waitpid
  • posix_spawn_file_actions_addclosefrom_np
  • close_range
  • lutimes
  • statvfs
  • chroot (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:

  • clone
  • unshare
  • setns
  • pivot_root
  • sys/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:

  1. Use jails as the primary isolation model. Linux namespace code paths are not portable as-is.

  2. Use nullfs + chroot/jail layout instead of Linux bind mounts + mount namespaces.

  3. Retain build users and classic UID/GID switching. These mechanisms remain directly usable on FreeBSD.

  4. Do not depend on Linux seccomp/capability/prctl machinery. Any comparable restrictions must come from a different design.

  5. Treat posix_fallocate conservatively. 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.