docs: storage plan — path is the id, label is queryable display metadata
Refine the naming decision: a volume's mount path IS its identity id (GPT GUID,
else fat-<serial>/mbr-<sig>-<index>) — a stable, unique, content-derived handle
software uses. The label (FAT volume label / GPT partition name) is mutable
display metadata, NOT in the path, exposed by a volume-manager `volumes` verb
returning {id, mount_path, label} — the database id/name split. This dissolves
the label-collision problem: same-label-different-id volumes get distinct paths
automatically; only identical ids (dd-clones) hit first-wins-and-log. S1 carries
both key (id) and label (display); S2 derives the id-path and adds the query.
This commit is contained in:
+59
-31
@@ -35,7 +35,8 @@ S5 removal robustness ── independent; single-volume ── may land any time
|
|||||||
comes FIRST because a volume's mount name is now its identity (below), and the
|
comes FIRST because a volume's mount name is now its identity (below), and the
|
||||||
friendly form of that name is the FAT label / GPT name S1 parses.
|
friendly form of that name is the FAT label / GPT name S1 parses.
|
||||||
- **S2** moves the last policy out of hardcode into `volumes.csv` +
|
- **S2** moves the last policy out of hardcode into `volumes.csv` +
|
||||||
`filesystems.csv`, and names each volume by its identity — no port-name.
|
`filesystems.csv`, and names each volume by its identity **id** (a GUID/key),
|
||||||
|
keeping the label as separate, queryable display metadata — no port-name.
|
||||||
- **S3** generalizes to N volumes across N devices.
|
- **S3** generalizes to N volumes across N devices.
|
||||||
- **S4** adds exFAT — a COMPLETE second engine that proves the V1 harness
|
- **S4** adds exFAT — a COMPLETE second engine that proves the V1 harness
|
||||||
extraction. Needs S2 (to route by signature) and S3 (to run a second volume).
|
extraction. Needs S2 (to route by signature) and S3 (to run a second volume).
|
||||||
@@ -52,7 +53,10 @@ rung-4 identity into a ladder that reads the richest available content identity:
|
|||||||
GPT partition GUID (rung 1, 128-bit), FAT volume serial + label (rung 3), MBR
|
GPT partition GUID (rung 1, 128-bit), FAT volume serial + label (rung 3), MBR
|
||||||
signature + index (rung 4, kept), bare-FAT (kept, enriched to its serial). The
|
signature + index (rung 4, kept), bare-FAT (kept, enriched to its serial). The
|
||||||
`u64` identity becomes a small tagged struct `Identity{ rung, key: u128, label,
|
`u64` identity becomes a small tagged struct `Identity{ rung, key: u128, label,
|
||||||
has_label }`. Because GPT metadata is at LBA 1 and the entry array beyond it, and
|
has_label }` — `key` is the **id** (the path handle), `label` is the **display
|
||||||
|
name** (the GPT 36-char partition name, or the FAT volume label), a separate
|
||||||
|
field per the id/name split S2 relies on. Because GPT metadata is at LBA 1 and
|
||||||
|
the entry array beyond it, and
|
||||||
the FAT serial is in each partition's VBR, `firstVolume` stops taking one
|
the FAT serial is in each partition's VBR, `firstVolume` stops taking one
|
||||||
preloaded block-0 slice and takes a `SectorReader` (context + read-one-sector
|
preloaded block-0 slice and takes a `SectorReader` (context + read-one-sector
|
||||||
fn, mirroring the engine's `BlockDevice` vtable) — host-testable against a
|
fn, mirroring the engine's `BlockDevice` vtable) — host-testable against a
|
||||||
@@ -97,20 +101,36 @@ never drift onto magic constants).
|
|||||||
## S2 — the mount map: volumes.csv + filesystems.csv
|
## S2 — the mount map: volumes.csv + filesystems.csv
|
||||||
|
|
||||||
**Goal.** Move the last two pieces of storage policy out of hardcode into
|
**Goal.** Move the last two pieces of storage policy out of hardcode into
|
||||||
configuration read by the volume manager. `filesystems.csv` (content signature →
|
configuration read by the volume manager, and split a volume's **id** from its
|
||||||
filesystem binary) so the VM picks the binary from the probed signature;
|
**label** (the database model: the id is the real, stable, unique key software
|
||||||
`volumes.csv` (identity → mount prefix, danos's fstab) as the explicit override
|
uses; the label is a mutable display name). `filesystems.csv` (content signature
|
||||||
for a volume the user wants at a fixed path. **The default mount name is the
|
→ filesystem binary) so the VM picks the binary from the probed signature;
|
||||||
volume's own identity, never a port or role name**: `/volumes/<label>` when the
|
`volumes.csv` (id → mount prefix, danos's fstab) as the explicit override for a
|
||||||
medium carries a label (the FAT label / GPT partition name S1 reads), else
|
volume the user wants at a fixed path.
|
||||||
`/volumes/vol-<hex-key>`; duplicate names follow the rationale's duplicate-
|
|
||||||
identity policy (first keeps it, second suffixed and logged). There is no
|
**The mount path IS the identity id, never a label or a port/role name.** A
|
||||||
`/volumes/usb` and no `/volumes/boot` — the boot volume is detected by content
|
volume mounts at `/volumes/<id>` — the GPT partition GUID for a GPT volume, a
|
||||||
(it installs the `/system/configuration` + `/system/logs` rewrites) but is NAMED
|
`fat-<serial>` / `mbr-<sig>-<index>` form otherwise (exact rendering is an impl
|
||||||
by its identity like any other. Parsed with `library/csv` exactly as
|
detail; the point is a stable, unique, content-derived string). Because the path
|
||||||
`device-registry` parses `devices.csv`. The VM hands the binary + volume-id +
|
is the id and never the label, two distinct volumes that happen to share a label
|
||||||
mount specs to fat at spawn (the argv channel V3b already uses for the volume
|
(`Backup`, `UNTITLED`, unlabeled) get distinct paths automatically and never
|
||||||
id); fat retires `fat_mounts` and reads mounts from argv[2..].
|
collide; only genuinely identical *ids* (dd-cloned media) hit the rationale's
|
||||||
|
duplicate-identity policy (first mounts, second suffixed and logged).
|
||||||
|
|
||||||
|
**The label is display metadata, exposed by a protocol query, not the path.** S1
|
||||||
|
reads it off the medium (FAT volume label, GPT partition name); a volume-manager
|
||||||
|
`volumes` verb returns `{ id, mount_path, label }` per volume so a future
|
||||||
|
shell/UI can show the friendly name — the id↔name split, like a table's primary
|
||||||
|
key vs its display column. (`volumes.csv` may optionally carry a chosen label
|
||||||
|
override alongside the path override, both keyed on id.) There is no
|
||||||
|
`/volumes/usb` and no `/volumes/boot`; the boot volume is detected by content (it
|
||||||
|
installs the `/system/configuration` + `/system/logs` rewrites) but is named by
|
||||||
|
its id like any other.
|
||||||
|
|
||||||
|
Parsed with `library/csv` exactly as `device-registry` parses `devices.csv`. The
|
||||||
|
VM hands the binary + volume-id + mount specs to fat at spawn (the argv channel
|
||||||
|
V3b already uses for the volume id); fat retires `fat_mounts` and reads mounts
|
||||||
|
from argv[2..].
|
||||||
|
|
||||||
**Key touchpoints.** New pure-logic modules `filesystem-map.zig` (parse +
|
**Key touchpoints.** New pure-logic modules `filesystem-map.zig` (parse +
|
||||||
`match(signature)`) and `volume-map.zig` (parse + `mountsFor(identity)` +
|
`match(signature)`) and `volume-map.zig` (parse + `mountsFor(identity)` +
|
||||||
@@ -123,21 +143,24 @@ argv; `fat.zig` deletes `fat_mounts`, parses argv[2..] into a bounded
|
|||||||
MBR disk signature so the boot volume's identity is a legible non-zero key.
|
MBR disk signature so the boot volume's identity is a legible non-zero key.
|
||||||
|
|
||||||
**Steps (commits).** (1) partition emits a signature. (2) `filesystem-map`
|
**Steps (commits).** (1) partition emits a signature. (2) `filesystem-map`
|
||||||
parser + host tests. (3) `volume-map` parser (identity→prefix override) +
|
parser + host tests. (3) `volume-map` parser (id→prefix override) + the id-path
|
||||||
identity-derived default naming (label→hex) + host tests. (4) Ship the tables +
|
deriver + the `volumes` label-query verb + host tests. (4) Ship the tables +
|
||||||
VM integration **behind fat's still-hardcoded mounts** (behavior-preserving —
|
VM integration **behind fat's still-hardcoded mounts** (behavior-preserving —
|
||||||
parsers proven before consumption flips; full suite green). (5) fat consumes
|
parsers proven before consumption flips; full suite green). (5) fat consumes
|
||||||
argv mounts; the VM names the boot volume by its identity (`/volumes/DANOS`, from
|
argv mounts; the VM mounts the boot volume at its id-path (`/volumes/fat-12345678`,
|
||||||
the FAT label S1 read); **migrate the fixtures + QEMU regexes off `/volumes/usb`**
|
from its serial); **migrate the fixtures + QEMU regexes off `/volumes/usb`**
|
||||||
(fat-test, badge-scope-test, vfs-test, and the four cases) in the same commit;
|
(fat-test, badge-scope-test, vfs-test, and the four cases) in the same commit;
|
||||||
the `volume-identity-name` QEMU case (shown failing against HEAD~1); flip the
|
the `volume-identity-name` QEMU case (shown failing against HEAD~1); flip the
|
||||||
docs' pending markers.
|
docs' pending markers.
|
||||||
|
|
||||||
**Discrimination.** QEMU `volume-identity-name`: the boot volume mounts at
|
**Discrimination.** QEMU `volume-identity-name`: the boot volume mounts at its
|
||||||
`/volumes/DANOS` (its FAT label), a line the old hardcoded `/volumes/usb` never
|
id-path (`/volumes/fat-12345678`, from its serial), a line the old hardcoded
|
||||||
emits. Host: an unlabeled identity derives `/volumes/vol-<hex>`; a `volumes.csv`
|
`/volumes/usb` never emits; the `volumes` query returns that id paired with the
|
||||||
override sends a mapped identity to its chosen prefix; `match(.fat)` returns the
|
label `DANOS`. Host: two volumes sharing a label but not an id get distinct
|
||||||
configured binary; fat's argv parser makes installed mounts a function of argv.
|
id-paths (the collision the label-as-name approach could not resolve stably); a
|
||||||
|
`volumes.csv` override sends a mapped id to its chosen prefix; `match(.fat)`
|
||||||
|
returns the configured binary; fat's argv parser makes installed mounts a
|
||||||
|
function of argv.
|
||||||
|
|
||||||
**Top risks.** Step 5's blast radius — a parser/argv bug, OR the `/volumes/usb`→
|
**Top risks.** Step 5's blast radius — a parser/argv bug, OR the `/volumes/usb`→
|
||||||
`/volumes/DANOS` migration missing a fixture/regex, breaks every fat-dependent
|
`/volumes/DANOS` migration missing a fixture/regex, breaks every fat-dependent
|
||||||
@@ -319,12 +342,17 @@ uses a geometry re-probe as the liveness oracle, which is correct but indirect.
|
|||||||
|
|
||||||
Both flagged decisions are settled:
|
Both flagged decisions are settled:
|
||||||
|
|
||||||
1. **Volume names are the identity (S2/S3).** Every volume mounts at
|
1. **A volume's path is its id; the label is display metadata (S1/S2).** The
|
||||||
`/volumes/<its-identity>` — the FAT label / GPT name where present, else the
|
mount path is the identity id — the GPT GUID, else a `fat-<serial>` /
|
||||||
identity-hex — never a port name (`/volumes/usb`) or a role name
|
`mbr-<sig>-<index>` form — a stable, unique, content-derived handle software
|
||||||
(`/volumes/boot`). `volumes.csv` remains the explicit override for a chosen
|
uses. The label (FAT volume label / GPT partition name) is a mutable display
|
||||||
fixed path. This makes S1 (which reads the label) precede S2, and folds the
|
name, NOT in the path; a volume-manager `volumes` verb returns
|
||||||
`/volumes/usb` → `/volumes/DANOS` fixture + regex migration into S2 step 5.
|
`{ id, mount_path, label }` so a UI can show the friendly name (the database
|
||||||
|
id/name split). Same-label-different-id volumes therefore never collide; only
|
||||||
|
identical ids (dd-clones) hit first-wins-and-log. `volumes.csv` overrides the
|
||||||
|
path (and optionally the label) for a chosen volume, keyed on id. Makes S1
|
||||||
|
precede S2 and folds the `/volumes/usb` → id-path fixture + regex migration
|
||||||
|
into S2 step 5.
|
||||||
|
|
||||||
2. **exFAT is implemented in full (S4).** A complete exFAT: full read and write,
|
2. **exFAT is implemented in full (S4).** A complete exFAT: full read and write,
|
||||||
directories, rename, and the on-disk up-case table for correct case-folding —
|
directories, rename, and the on-disk up-case table for correct case-folding —
|
||||||
|
|||||||
Reference in New Issue
Block a user