Files
fds-os/docs/developer/rootfs.md
T
2026-09-22 13:23:34 +08:00

12 KiB

Building and using the ARM root filesystem

Development reference and historical context. For current operating instructions, use the user manual. Acceptance applies only to the source and artifacts identified in each record.

Documentation index · First build · Validation

The assembler produces a configured aarch64 glibc Linux userspace at out/rootfs-aarch64.tar. It contains GNU tools, XBPS, the s6 tools, the cartridge manager, optional desktop stack, and the Dasung Paperlike 13K controller. You can inspect it and run its ARM programs on the build workstation. M2 adds native s6 init, runtime mounts and getty. The tar itself has no bootable partition table or Pi boot volume; do not flash the tar to a disk. Follow Native init and ARM VM to boot it and open a development shell. M12 also adds make rootfs PROFILE=recovery and make recovery; see Recovery for its distinct local maintenance console and read-only cartridge defaults. Each build preserves a profile-specific out/rootfs-PROFILE.tar pointer in addition to the latest-rootfs pointer.

1. Prepare the workstation

Use the x86_64 Arch setup in Your first build. M0 should pass before you build M1:

make bootstrap
make smoke-test
make check

M1 also needs Python 3.14 or newer on Arch. It inspects XBPS metadata and zstd package archives and produces the tar using only the standard library:

sudo pacman -S --needed python
python3 --version

The rootfs builder installs qemu-user-aarch64 and util-linux into the existing project-local Void build container. Static QEMU executes ARM programs; util-linux supplies namespace-local mount and capability setup. Linux 6.7+ provides a private binfmt_misc registry inside the new user namespace, allowing ARM child programs to execute without PRoot's syscall translation. Mount/setup capabilities are dropped before package scripts run; only namespace-local file-capability support remains. These are build tools, not additional target requirements. No host-wide binfmt registration, sudo chroot, or host boot configuration is needed. The Dasung musl compiler dependencies remain documented in Dasung integration.

Allow several additional GB for package downloads, staging trees, the archive, and retained inputs. Build commands share one Void container; run them sequentially.

2. Build the CLI base

From the checkout root:

make rootfs PROFILE=cli

The first run downloads signed Void ARM packages and may take several minutes. Later runs reuse the download cache, rebuild the local FDS packages, and assemble a fresh root. Success ends with:

PASS: FDS rootfs built and verified: out/rootfs-aarch64.tar

cli boots to the ordinary-user FDS console and includes optional desktop and Ethernet services. They start only when requested by the user or eligible media. PROFILE=development adds native C/C++/Rust compilers, build systems, Git, debuggers and Vim, plus virtual display diagnostics; see Development and the desktop guide. Each archive embeds its profile identity. Unknown profiles and mismatched SYSTEM image labels fail explicitly.

Output Purpose
out/rootfs-aarch64.tar Exported root filesystem, with target ownership, permissions and file capabilities
out/rootfs-aarch64/ Staging tree for inspection and emulated commands; owned by your host user
out/manifests/rootfs-latest/ Exact package list, checksums, metadata, scripts and build inputs
out/manifests/rootfs-latest/packages/ Copies of every selected XBPS package and available upstream signatures
out/packages/fds-base-0.1.0_1.aarch64.xbps Explicit base dependency metapackage
out/packages/fds-base-files-0.1.0_1.aarch64.xbps FDS identity, accounts, defaults and layout specification
out/logs/rootfs-build.log Package installation, configuration and acceptance output

The first three paths are links into one retained out/rootfs-build.* directory. A failed build keeps its workspace for diagnosis and does not publish a partial archive over the last successful one. Copy the tar itself when moving the result to another machine; copying only the link does not copy the artifact.

3. Inspect the result

tar -tf out/rootfs-aarch64.tar | head -40
cat out/rootfs-aarch64/etc/os-release
cat out/manifests/rootfs-latest/packages.json
(cd out/manifests/rootfs-latest && sha256sum -c rootfs.sha256 packages.sha256)

The default hostname is fds, the timezone is UTC, and the default language is English (en_US.UTF-8). Account passwords are locked and no SSH server starts. CLI and development boot to the local fds account (UID 1000) through autologin; recovery has a local root maintenance console. The build shell below runs as emulated root so it can inspect and configure the staging tree. It is separate from the normal booted user session.

4. Run ARM programs on the workstation

./tools/in-rootfs out/rootfs-aarch64 /usr/bin/ls --version
./tools/in-rootfs out/rootfs-aarch64 /usr/bin/readelf --version
./tools/in-rootfs out/rootfs-aarch64 /usr/bin/xbps-query -l
./tools/in-rootfs out/rootfs-aarch64 /usr/bin/dasungd check
./tools/in-rootfs out/rootfs-aarch64 /bin/bash --login

Inside the shell, try getconf GNU_LIBC_VERSION, locale charmap, ls /usr/bin, or xbps-query fds-base. Type exit to return to the workstation.

This runs ARM userspace on the host kernel through QEMU. It does not boot an ARM kernel or validate Pi drivers. Bubblewrap makes the target the actual process root, supplies minimal devices, and mounts temporary /run and /tmp. Native emulation tools are mounted read-only under /tmp/fds-build-host. Physical USB and DRM are absent, so this helper cannot control your monitor. Use it for trusted image-build commands. Do not use this shell to test disk formatting, mounts, power-off, or real device access.

Commands can modify the staging tree. Those changes do not change the exported tar. Rebuild to get a clean staging tree and archive. Built SYSTEM images remain read-only: change packages at image build time, not by upgrading a live SYSTEM. For the booted target's on-demand Ethernet and DNS policy, see Desktop and Ethernet.

5. Run the acceptance checks

make rootfs-test

This verifies the archive and saved package checksums, restores a disposable tree, and runs ARM glibc, GNU tools, a shell pipeline, locale, XBPS integrity, Dasung configuration, and s6 database queries under QEMU. It also checks every ELF file for the target architecture, validates archive permissions and ping capabilities, and tests rejection of forbidden packages, unfinished configuration, wrong ABIs, a host executable, missing boot entries, a shell init replacement, and unsupported profiles.

The image has no BusyBox, systemd, runit, or musl runtime package. Dasung carries its own statically linked musl and libusb. Its service database is compiled at build time and includes the base monitor bundle. M2 boots the current graph under native s6 PID 1 in a full ARM VM. Physical monitor recovery still requires a Pi test.

What goes into the base

packages/fds-base/template owns the explicit dependency set. The assembler also consumes image/base-packages.list, so the required hardware package cannot be lost when selecting a profile.

Package group Reason
glibc, glibc-locales GNU C runtime and compiled English UTF-8 locale
coreutils, binutils, findutils, diffutils, grep, sed, gawk, bash, tar, gzip, xz GNU command-line tools, ELF inspection, scripting and archives
util-linux, procps-ng, shadow, kbd Standard system, process, account and console utilities
e2fsprogs, dosfstools, erofs-utils Tools for the planned ext4, FAT and EROFS media
eudev, kmod Device discovery tools and module management
iproute2, iputils Network inspection and configuration tools
pciutils, usbutils Hardware inspection
ca-certificates, tzdata, xbps TLS trust, timezones, and the package database/tools
skalibs, execline, s6, s6-linux-init, s6-rc Native init, process supervision, and service dependency tools
fds-base-files, fds-init, fds-dasungd FDS defaults, native init/service graph, and the mandatory monitor controller

XBPS resolves shared libraries and other transitive dependencies. The exact resolved set is in packages.json; do not mistake the direct dependency list for the entire installed set. Planned fds-cli and fds-cartridged packages are deliberately absent until their implementation milestones.

Assembly details

The FDS base-files package supplies usr/share/fds/layout.json. The assembler applies that manifest before XBPS extraction to create merged /usr links and empty mountpoints. Upstream xbps-src reserves these links for Void's own base-files; this approach keeps its tracked files and lint rules unchanged.

Remote packages are signature-checked using Void keys from the pinned checkout. Local FDS packages are built here and are unsigned development artifacts. A foreign-architecture XBPS transaction unpacks them; the target's own XBPS then configures every package under QEMU. Locales, CA certificates, the udev hardware database, the dynamic linker cache, native init environment, and the complete s6 service database are ready before export. Certificate generation also runs as an explicit checked step: the upstream CA package wrapper suppresses updater errors. There is no first-boot package configuration step.

Unused upstream runit service files are excluded by the shipped XBPS noextract rules. This does not replace upstream package templates or add runit to the image.

The staging directory cannot preserve arbitrary target UID/GID values as a normal host user. Export restores ownership from the exact XBPS archive headers, applies the util-linux wall/write tty-group and XBPS xbps-uchroot xbuilder-group adjustments, and gives generated files root ownership. Namespaced Linux capabilities are converted to the portable root-owned encoding. The archive, not the host-owned staging tree, is the authority for installation metadata. When eventually restoring as root, use GNU tar with --numeric-owner --same-permissions --xattrs --xattrs-include=security.capability. For complete disk images and the deferred physical installation procedure, see Internal storage. Do not write this rootfs tar to a disk.

The rootless configuration log therefore reports group-change failures for wall, write, and xbps-uchroot. These three known adjustments are applied and checked in the exported archive. Other installation errors require investigation. The builder also reapplies and verifies ping's CAP_NET_RAW with direct QEMU inside a user namespace and checks the actual stored xattr.

Package versions, input hashes and selected archives are retained for inspection. The Void binary repository is rolling; a fresh build later may resolve different versions. Those records alone do not establish reproducibility. Use the separate frozen-input and offline rebuild workflow for release construction and consult its recorded acceptance status.

If a build fails

Read the last error in out/logs/rootfs-build.log; keep the printed build-workspace path. A package setup error is a build failure, not a reason to publish an unconfigured filesystem. Correct the issue and run the build again; downloads already in out/cache/rootfs are reused.

If you changed an overlay and receive Stale overlay, compare your edited packages/fds-* directory with the named generated copy under vendor/void-packages/srcpkgs/. Move only that generated copy out of the way and rerun. Never discard tracked upstream changes to silence the pin check. See Troubleshooting for the existing build-container checks.

References: Void chroot installation, XBPS configuration, Linux binary-format handlers, and QEMU userspace emulation.

The rootless exporter restores ownership changes that package install scripts cannot represent in a single-UID user namespace. This includes tty ownership for wall/write, the xbuilder group for xbps-uchroot, and the utmp group for the terminal-accounting helper. Archive checks validate these identities explicitly; the ARM boot check also inspects the installed helper's actual group.