FDS/OS 1.0

This commit is contained in:
2026-09-21 22:29:23 +08:00
commit 99bc3d15c5
430 changed files with 34876 additions and 0 deletions
+158
View File
@@ -0,0 +1,158 @@
# M9 media-tool validation
[Media-tool usage](media-tools.md) · [Implementation ledger](implementation-status.md)
M9 software acceptance passed on 2026-09-21. Both packaged profiles, ARM image
construction, confirmed virtual USB writes, SYSTEM creation/write/boot, and the
earlier-milestone regression checks passed. All devices in these tests are
disposable regular files exposed only to isolated ARM virtual machines. Physical
Pi, media-controller and display tests remain deferred.
## Verified behavior
`make media-image-test` created SYSTEM, DATA, PROGRAM and ENVIRONMENT images with
the actual static ARM `fds-burn`. Independent `sfdisk`, Python SHA-256,
`fsck.erofs` and `e2fsck` checked the outputs. Invalid GPT extents, extra partitions,
internal partition labels and inconsistent backups were rejected even when the
fixture recomputed valid CRCs. Existing output files and recursive source/output
layouts were refused. Evidence: `out/m9-images.zqojr8hc/`.
`make media-test` verified these cases through the ordinary FDS user's CLI:
- Inspection reports the bay's capacity, logical sector size, model, serial and
kernel insertion identity. The running SYSTEM and mounted media refuse writes.
- Preview and cancellation leave the target unchanged. A confirmation is bound
to its operation, bay, insertion and image hash; a wrong phrase cannot proceed.
- Changing source bytes after preview is detected before writing. Replacing the
device in the same bay invalidates the old confirmation and leaves the new
insertion untouched.
- A PROGRAM image is written, flushed, read back and marked SAFE. Its backup GPT
moves to the end of a larger device. SAFE survives a daemon restart; a new
insertion mounts normally and supports explicit unprivileged execution.
- PROGRAM, DATA and ENVIRONMENT images are created on the running ARM machine.
DATA permits ordinary-user writes, retains data across removal/reinsertion and
passes independent host `e2fsck` after safe eject.
- Cartridge status remains usable while a separate write worker is active.
Source DATA cannot be ejected while the worker holds its image, including
across the worker's private mount namespace.
- Killing the cartridge daemon during an awaiting-confirmation operation leaves
a failed record on restart. It never becomes SAFE or automatically remounts.
- A valid EROFS/GPT containing an approximately 60 KiB malformed manifest is
rejected before writing. Its diagnostic stays bounded and printable, the
daemon retains the same process ID, and subsequent status requests work.
The final virtual USB evidence is `out/m9-vm.cdlj6_r7/`. The crash case uses native s6
supervision controls; see the [upstream s6-svc interface](https://skarnet.org/software/s6/s6-svc.html).
## SYSTEM creation through boot
`tests/integration/m9-system.py` mounts a complete prepared SYSTEM after boot,
then runs the real ARM filesystem/image builder against that read-only tree.
The ordinary user previews and confirms a write to another virtual cartridge.
After device flush and readback verification, a fresh VM boots the written disk
through the actual Pi kernel, stage0 and native s6 to the ordinary FDS prompt.
The test verifies UID 1000, read-only SYSTEM behavior, Dasung, the preserved
`root:utmp` terminal-helper ownership, and packaged Terminus caches without a
new user cache. Final evidence: `out/m9-system.ej8oz_av/`.
This demonstrates creation and boot of a usable SYSTEM on ARM, not just a valid
partition header. It does not emulate Raspberry Pi firmware, physical NVMe/RP1,
USB power behavior or Dasung video output.
## Implementation and dependencies
`fds-burn` is static-musl Rust. `fds-inspect` and `fds-eject` are static aliases of
`fds`; the daemon starts a bounded worker for each media operation. The worker
opens the exact disk with exclusive access, checks live identity again before
writing, validates the image's filesystem and manifest, and verifies actual
readback after flushing and invalidating the device cache. A root-owned record
prevents interrupted operations from acquiring a false SAFE status after restart.
Protection checks cover mounts, swap, kernel holders and the three reserved
internal partition names. Existing primary and backup GPTs are also inspected
directly, so protection does not depend solely on partition uevent timing. Native
4 KiB logical-sector disks are explicitly unsupported by this writer.
The eight new locked Rust dependency entries are explained in the
[media guide](media-tools.md#dependencies-and-current-evidence). Filesystem tools
and GNU coreutils already belong to the base image; their package dependencies
are now declared explicitly. No init replacement, background network service,
new native library dependency of an FDS executable, or boot-time cache builder
was introduced. Void's tracked source files remain unchanged.
## Findings resolved during validation
A private checker device node initially failed to open because `/run` is mounted
with `nodev`. The final code creates an inaccessible private directory on
`devtmpfs`, opens its read-only device inode, and immediately unlinks it. The
filesystem checker receives that handle and runs as UID 1000; global `/dev/loopN`
permissions are unchanged. Image mounts exist only in the worker's private mount
namespace and disappear when it exits.
The namespace review also identified the need to block eject until the worker
exits: unmounting only the daemon's namespace could otherwise report a source
DATA cartridge SAFE while the worker retained its mount. The VM test now covers
that exact case.
Malformed TOML can include most of its source text in an error message. Worker
diagnostics are now limited to 2048 UTF-8 bytes plus a truncation suffix, with
control characters replaced by spaces. This prevents cartridge contents from
overflowing the daemon's status protocol or injecting terminal controls.
The sustained-write regression initially failed because kernel I/O diagnostics
interrupted CLI JSON in the middle of a serial line during deliberate removal.
The test now executes each command once, saves its stdout, and transfers it with
base64 and a SHA-256 checksum. A damaged transfer is retried without rerunning
the command. Kernel output remains in the serial log and a separate diagnostic
capture. The corrected test passed in `out/m7-vm.t5d5fd_4/`, including independent
ext4 checks and the exact 64 MiB write payload.
## Packaged artifacts and regressions
The rebuilt CLI rootfs contains 180 packages and 1393 AArch64 ELF files. All FDS
executables remain static-musl. Package archives, input hashes, ownership and
capability records are retained beside the rootfs.
| Artifact | Location | SHA-256 |
| --- | --- | --- |
| CLI rootfs, 498616320 bytes | `out/rootfs-build.gs9ZTA/rootfs-aarch64.tar` | `4abbc4e4e1823ef78acde2645d915160aa8e1c0f8b3173e8bc73f8d6b923d502` |
| CLI SYSTEM | `out/system-build.a74aryls/system.img` | `b7a71dad45c259c92ec5a5ac72b87356132f642df5c33d74d20e2e6ca564dee6` |
| FAT32 boot partition | `out/boot-build.oapz3fuf/boot.fat` | `ac3ee864c6293dd30a640fbbc0514b08acb92cda2d25d05128d234229308ac8f` |
The earlier-milestone regression run passed tooling, formatting/build checks,
rootfs rejection checks, native init, the stage0 boot matrix, console and
cartridge tests. Its logs are `out/logs/m9-final-*.log`; the corrected DATA run
is `out/logs/m9-data-retry3.log`. The final diagnostic change was then rebuilt
into the CLI archive and passed the complete media write/boot and archive checks
in `out/logs/m9-hardened-*.log`.
Native init evidence is `out/m2-vm.ijzHZC/`, the boot matrix is
`out/m4-vm.sxNb8D/`, console checks are `out/m5-vm.6suv4qjj/`, and cartridge
checks are `out/m6-vm.o5sjhatc/`. Kernel-to-console was 1854.858231 ms in this
VM run, compared with M8's 1854.724813 ms: **+0.133418 ms**. The actual static ARM
comparison passed without an exception; see `out/logs/m9-boot-comparison.log`.
This is below the 100 ms review threshold and is not a Pi performance result.
The development rootfs contains 191 packages and 1405 AArch64 ELF files.
`make rootfs-test`, `make desktop-test` and development SYSTEM construction passed,
followed by the final tooling and formatting/build checks. Evidence is
`out/m8-vm.498wsxsv/` and `out/logs/m9-hardened-*.log`. The screenshot was inspected
and retains the grayscale Terminus terminal. First desktop activation was
1111.300247 ms in this VM; physical display timing remains unmeasured.
| Artifact | Location | SHA-256 |
| --- | --- | --- |
| Development rootfs, 501882880 bytes | `out/rootfs-build.sQCq5V/rootfs-aarch64.tar` | `87e9a35829432f07e4a4bfa4c69eabd5ba0f30a25eadfed05ea9393bd1fb2afa` |
| Development SYSTEM | `out/system-build.l5e3j1pg/system.img` | `bcb8dfa7ec6e3207080f5c68074cc0c27d1bea63a0b51561c8ec628691e03611` |
The generic rootfs convenience links were restored to the CLI build after the
development checks. `out/rootfs-cli.tar` and `out/rootfs-development.tar` retain
explicit pointers to these two verified archives.
## Deferred and remaining acceptance
Physical media throughput, flash-controller write-cache behavior, USB brownouts,
power-loss durability and Pi boot remain deferred. No throughput or hardware
latency claim is made. Broader I/O failure injection and twelve-device stress
belong to M11; ordered power-off and persistent DATA error handling belong to M10;
signing, independent recovery and complete release inputs belong to M12.