fat/harness: filesystems coexist without the shared vfs name; two-volume proof (S3)
A second usb-storage device (a generated data volume, serial da7a0001, an empty FAT with no /system) plugged in beside the boot volume: the volume manager adopts both devices and spawns a confined fat per volume, each mounted at its own content id-path. The test surfaced a real coexistence bug. Every filesystem bound the single "vfs" contract name under /protocol; the second volume's fat lost the race, service.run refused-and-exited on the held name, and that volume never mounted. Clients don't reach filesystems by that name — fs_resolve routes a path to its backing endpoint through the kernel mount table by prefix — and nothing consumes "vfs", so the fix is to bind no shared name: the harness's service_name now defaults to null. This is the "this fades" the harness comment anticipated for the volume-manager era; a filesystem's endpoint still serves as its mount backend without a name. fat logs "is a data volume" for the non-system branch so the test can positively assert content-based detection. make-fat-image gains --serial/--label (default unchanged) so a second image gets a distinct id-path; the data image is generated per run, never committed. The case fails against the pre-fix harness (the data volume's fat exits on the refused bind) — toggle-demonstrated. Full suite 129/129 (128 + two-volumes); the single-volume path is unaffected by dropping the vestigial name bind.
This commit is contained in:
@@ -816,6 +816,27 @@ CASES = [
|
||||
"expect": r"volume-manager: volume 0x0*12345678 -> \S+ \(pid \d+\), lba \d+, \d+ blocks"
|
||||
r"[\s\S]*volume-manager: handed volume \d+ to pid \d+",
|
||||
"fail": r"DANOS-TEST-RESULT: FAIL"},
|
||||
# S3 multi-volume: a SECOND usb-storage device (a generated data volume, serial
|
||||
# da7a0001, an empty FAT with no /system) plugged in beside the boot volume.
|
||||
# Proves the volume manager adopts BOTH devices and spawns a confined fat per
|
||||
# volume, each mounted at its own CONTENT id-path (/volumes/fat-<serial>); and
|
||||
# that boot-volume detection is by content — only the volume that carries
|
||||
# /system backs /system/configuration, while the data volume mounts at its
|
||||
# id-path alone. Against the pre-S3 one-device/one-volume manager the data
|
||||
# volume never mounts, so the da7a0001 lookaheads fail (toggle-demonstrated by
|
||||
# checking out the step-2 volume-manager.zig).
|
||||
{"name": "two-volumes",
|
||||
"build_case": "fat-mount",
|
||||
"smp": 4,
|
||||
"timeout": 150,
|
||||
"data_volume": {"serial": "DA7A0001", "label": "DATAVOL", "size_mib": 64},
|
||||
"expect": r"(?s)(?=.*volume-manager: volume 0x0*12345678 -> )"
|
||||
r"(?=.*volume-manager: volume 0x0*da7a0001 -> )"
|
||||
r"(?=.*fat: mounted /volumes/fat-12345678)"
|
||||
r"(?=.*fat: mounted /volumes/fat-da7a0001)"
|
||||
r"(?=.*carries the system tree)"
|
||||
r"(?=.*data volume; mounted at /volumes/fat-da7a0001)",
|
||||
"fail": r"data volume; mounted at /volumes/fat-12345678|DANOS-TEST-RESULT: FAIL"},
|
||||
# Phase 2b: mkdir/unlink through the mount. Reuses the fat-mount build — the
|
||||
# fat-test client, after listing, makes a directory, writes+reads a file inside
|
||||
# it, then removes the file, exercising the whole VFS -> fat mutation path.
|
||||
@@ -1422,6 +1443,23 @@ def run_case(arch, case):
|
||||
cmd[cmd.index("-m") + 1] = case["mem"]
|
||||
if case.get("qemu_extra"): # extra qemu args, e.g. -device intel-iommu for the IOMMU case
|
||||
cmd += case["qemu_extra"]
|
||||
# A multi-volume case attaches a second usb-storage device backed by a freshly
|
||||
# GENERATED data volume: a distinct-serial FAT32 with no /system tree, so the
|
||||
# volume manager mounts it at its own id-path and the fat process marks it a
|
||||
# data volume (never a system volume). Regenerated per run — no image is
|
||||
# committed to the tree (the user keeps the boot files copyable, not baked in).
|
||||
if case.get("data_volume"):
|
||||
dv = case["data_volume"]
|
||||
data_img = os.path.join(WORK, "data-volume.img")
|
||||
subprocess.run(
|
||||
[sys.executable, os.path.join(REPO, "tools", "make-fat-image.py"),
|
||||
"--serial", dv["serial"], "--label", dv.get("label", "DATAVOL"),
|
||||
data_img, str(dv.get("size_mib", 64))],
|
||||
check=True, stdout=subprocess.DEVNULL)
|
||||
cmd += [
|
||||
"-drive", f"if=none,id=datausb,format=raw,file={data_img}",
|
||||
"-device", "usb-storage,bus=xhci.0,port=4,drive=datausb,removable=on,id=datastorage",
|
||||
]
|
||||
# A QMP control socket, always present (additive): how a case's `qmp_after`
|
||||
# hook injects host-side events into the guest mid-run. Kept under a short temp
|
||||
# dir, not WORK: a unix socket path is capped at ~104 bytes (sun_path), and a
|
||||
|
||||
Reference in New Issue
Block a user