Skip to content

ToyOS designates its own DATA partition: one GPT writer, the driver judges and stamps under a signed image's authorization - #1085

Merged
Japabu merged 14 commits into
mainfrom
partitions
Oct 28, 2026
Merged

Japabu merged 14 commits into
mainfrom
partitions

Conversation

@Japabu

@Japabu Japabu commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

ToyOS could not touch its own disks' partitions. The benches' DATA partitions were to be stamped from the bench's Linux: #1079 proposed toyos-metal --designate-data, which is unmerged and has since dropped it. Scratch sticks are made with sgdisk (--adopt-scratch). A foreign DATA partition was answered with memory (issues/a-foreign-data-partition-is-answered-with-memory.md). Owner direction (2026-10-28): ToyOS manages its partitions itself.

What this builds

  • One GPT writer, in toyos-gpt (toyos_gpt::edit). It covers the table model, everything UEFI 2.10 §5.3 requires of a table, encoding with both CRCs, and the write order §5.3.2 requires ("Software must update the backup GPT before the primary GPT"): backup, flush, primary, flush. It also reads each copy alone, so a caller sees which copy is in force and whether the two agree. build/gptwrite.rs is deleted; the build and the harness lay tables with it.
  • Drivers read their table to the disk's true last sector. nvme and usbd floored the count to whole 4 KiB blocks, so on a disk whose sector count is not whole blocks (one bench stick: 7507812 sectors) the backup was never where the parser looked. The parser's count-floor concession (lba_count_granularity) has no reader left and goes.
  • The driver that holds a disk designates its partition itself (toyos_blockserve::designate), so the identity checked is the identity written through.
    • Refused before anything is taken: a held partition (the existing holds), a partition the running system is on (--active, from the loader's inventory), and one the table does not name or serve.
    • The steps: read the front 1 MiB; judge it (toyos_blockserve::content: ToyOS's own volume, an existing stamp, and named foreign signatures, as a bug fence, never a licence); write one whole stamp block made in memory; flush; read it back and compare.
    • It runs through the same Device as every session, so nvme and usbd need no code of their own.
  • diskserver routes by badge (wire::Badge). Tables lists every partition and has a driver describe its disk. DesignateData does one thing: the machine's one DATA partition, resolved when it is asked for (zero, two, or a GUID on two disks is refused). Neither opens a session.
  • /system/bin/disks: disks list and disks designate data [--commit] (without --commit it judges and writes nothing). It holds no authority of its own. Each command needs the authorization its row is minted (toyos_manifest::authorize). The supervisor mints it at each start of the row from the signed image's authorize = [...].
  • The stamp's one definition is bcachefs::designation / designated, used by the build, the file server and the drivers.
  • A foreign DATA partition is served absent, not from memory: /apps, /config, /home and /state are absent on that boot. That closes issues/a-foreign-data-partition-is-answered-with-memory.md; hostdisk::left_alone reads the absent line and that the in-memory line is never said.

Authorization: what is and is not built

  • Built: the preauthorized job only.
    • The build refuses an authorization that changes a disk to any row a login can start and to any login authority, whose namespace reaches every program a login runs directly.
    • A host test, no_shipped_boot_config_authorizes_a_disk_change, holds the shipped configs to carrying none.
    • Today only tests/images/designatecase.toml's runner holds designate:data. The shipped disks row holds tables (read only).
    • The connector designates the one DATA partition each time it is used, for the row's life. Each use is resolved and checked when it happens.
  • Not built: interactive mutation. disks changes nothing for a person at a shell or over ssh, and a login grants nothing. Its exit is the administrative authorization every system change will share (planning architect, 2026-10-28), recorded as stage 1 of issues/toyos-manages-its-own-partitions.md. Nothing disk-specific stands in for it.
  • No token. An image's preauthorization names a role, not a state a person reviewed, so the designation is resolved and judged in the turn it is carried out. A token bound to the disk's state comes with plan-bound authorizations (table edits, stage 2).

Retires

  • The host stamp osbench: the comparison measures the systems, not the setup #1079 proposed (toyos-metal --designate-data, unmerged, now dropped there): the bench_data_designated row stamps from inside ToyOS instead. Nothing on main had it.
  • --adopt-scratch, once Init lands (stage 3). Its table is not served today, because sgdisk's partition is not whole 4 KiB blocks (issues/the-scratch-disk-adopt-scratch-makes-is-not-served.md, read from a bench boot's log).

Checks

Each line gives the command, the exit code and the revision it ran at.

  • Round 3, at 05720ad63: nvme_leaves_the_benchs_disk_alone now claims nothing on the bench's disk is touched but its ToyOS DATA partition. DATA is left alone while foreign, formatted or mounted as bcachefs once designated, and never stood in for by memory.
  • Round 2, at 0343c0aa8:
    • cargo run -- --ci host: exit 0, 105 steps green.
    • cargo run -- --build-only: exit 0.
    • QEMU x86-64 (cargo test --test toyos-build -- the_data_partition_beside_the_stick_is_designated a_host_disk_beside_the_stick_is_left_alone): exit 0.
    • cargo test of -p toyos-gpt, toyos-blockring, toyos-blockserve, diskserver, disks, toyos-manifest, bcachefs, toyos-xhci, fileserver and toyos-metal: exit 0 each.
  • Round 1:
    • --ci host exit 0 at fe22e03c6. The first run, at a72018eb9, exited 1 on an uncovered config and a redundant clone.
    • QEMU pair exit 0 at 3757d0da1.
  • Negative controls: each applied as a patch, built, run and reverted.
    • No middle flush: the torn-write model reds (both copies torn, no table).
    • Primary first: it reds on the order property.
    • Stamp at block 1: the QEMU host oracle reds at DATA's block 0, and so does the unit test.
    • usbd's floored count put back: the tail-read test reds ("the backup on sector 16387 was not read").
    • The login check removed from the gate: a_disk_changing_authorization_goes_to_no_row_a_login_can_start reds.
  • QEMU's oracle: the NVMe driver says designation of …: Written. The host reads the disk image after the stop: DATA's block 0 is exactly bcachefs::designation, and no other byte changed.
  • QEMU targets: x86-64 q35, Profile::MetalBesideAHostDisk. No AArch64 configuration carries that disk.
  • Metal, AMD bench: bench_data_designated judged PASS at 0343c0aa8. Boot 1: the NVMe driver wrote and read back the stamp on p4 (600 GiB). Boot 2: the file server formatted it and served bcachefs. The next boot (osbench: the comparison measures the systems, not the setup #1079's benchcase at fa7258221) mounted it: fileserver: mounted the DATA volume, 11 names. Evidence: https://github.com/ToyOSOrg/evidence/tree/f5f366507350e3edf5f1ab4fdd5408c2d9aa14ab/partitions/amd
  • Metal, T14: bench_data_designated judged PASS at 0343c0aa8. Evidence: https://github.com/ToyOSOrg/evidence/tree/b4415902d279607eb788e57da9f4c0ec3aa690f8/partitions/t14

Risks

  • Run the designation first on both benches. Any boot whose DATA is still foreign serves /apps, /config, /home and /state absent. bench_data_designated is staged for the T14 and the AMD bench.
  • No mid-format reboot in the second boot. The supervisor makes the session home before it starts the first non-storage row (system/supervisor/src/main.rs, make_session_home on its file worker, bounded by FILES_MS). DATA's file server formats before its first answer, so the runner starts after the format.
  • A first format is not superblock-last on the medium. The file server's cache writes block 0 first, so a machine that stops during that one flush could leave a superblock over an unwritten bitmap. This is a property of the existing first-boot format, filed as issues/a-first-format-interrupted-mid-flush-can-leave-a-superblock-over-a-bitmap-it-never-wrote.md.
  • A stop's sync is not recorded. The supervisor makes the log whole before it syncs the file servers, so a readback cannot say whether a stop made DATA durable. Both benches' formats were confirmed by their next boots instead: fileserver: mounted the DATA volume, bcachefs, 11 names. Filed as issues/a-stops-file-sync-and-its-failure-are-never-recorded.md.
  • USB. The T14's boot stick has 7,507,812 sectors, four past its last whole block. It is flashed with a smaller image, so its last sector holds no backup, and usbd now says the copies disagree on every T14 boot. That is true, and it is the short final READ(10) answering. The T14 judge refuses a ReadFailed there. Designation on a USB disk itself is host-tested only.

🤖 Generated with Claude Code

Japabu and others added 11 commits October 28, 2026 10:05
`toyos_gpt::edit` is the one GPT writer in the tree, which the disk drivers
will edit their own tables with: a `Table` of 128 entries of 128 bytes,
checked against everything UEFI 2.10 §5.3 requires (usable range, ranges,
overlaps, zero and duplicate unique GUIDs, unused entries all zero), encoded
with both CRCs, and laid by `Encoded::steps` as §5.3.2 requires: "Software
must update the backup GPT before the primary GPT" — backup array and
header, a flush, then LBA 0 when asked and the primary, then a flush.
`read_copies` reads LBA 0 and each copy alone, so a caller sees which copy is
in force and whether the two agree, rather than the first one that parsed.

The flush between the copies is what keeps a crash from tearing both: the
host model cuts the write after every step and inside every write at any
subset of its sectors, and each cut reads through `toyos_gpt::list` as the old
table or the new one, never neither or a mixture, with the primary never
newer than the backup. Without the middle flush a cut leaves both copies
failing their CRCs; with the primary first, the primary is newer than the
backup — under this reader alone that still reads as old or new, which is
why the order is asserted as the specification's own invariant.

`build/gptwrite.rs` goes: the build and the harness lay their tables with
`image::write_gpt` over this writer, and the byte-for-byte oracle against
the `gpt` crate moves to `lib/gpt/tests/edit.rs`, where that crate is
already a dev-dependency.

The designation stamp's encoder and reader are `bcachefs::designation` and
`bcachefs::designated`, beside the magic they share, so the build, the file
server and the drivers that will write it have one definition.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… count floor goes

UEFI 2.10 §5.3.1 puts the backup header on the device's last LBA. nvme and
usbd read their table through a 4 KiB-block view whose count they floored,
so on a disk whose sector count is not whole blocks — one bench stick has
7507812 512-byte sectors — the backup was never where the parser looked,
and `toyos_gpt::Sectors::lba_count_granularity` existed to concede the
difference. Both now read every sector: whole blocks where the block is
whole, and the tail one sector at a time (nvme's `read_sectors`, usbd's
READ(10) of the sectors the last block has). The concession has no reader
left and goes, with the floored-device test machinery.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…self, under a token

A disk driver is the one process that holds its device and reads its table,
so it is where a designation is judged and carried out: the identity
checked is the identity written through, with no path between.

- Each disk's table state has a 128-bit random token, drawn at bring-up and
  again after every change the driver makes to the disk
  (`wire::Token`, `toyos_blockserve::draw_token`). An authorization is bound
  to one; a re-plug, a driver restart or a change made since refuses it
  `Stale`, so one is used at most once.
- `Sessions::describe` answers a disk's size, its token, LBA 0 and each copy
  of the table as `toyos_gpt::edit::read_copies` read them, and every entry
  of the copy in force with whether a session holds it (`MSG_DISK`,
  `MSG_SLOT`, `MSG_DESCRIBED`).
- `Sessions::designate` refuses a stale token, a partition the running
  system is on (`active`, the service's word) and one a session holds, then
  holds the partition for the job's length and runs
  `toyos_blockserve::designate`: read the front 1 MiB, judge it, and only for
  a partition it does not recognise and a request that commits, write the
  stamp as one whole block made in memory, flush, read it back and compare.
  `toyos_blockserve::content` refuses ToyOS's own volume, an existing stamp
  and the signatures of the formats it names: a bug fence, never a licence,
  since recognising nothing proves nothing.
- `Link` hands over describe and designate work beside sessions
  (`disk::MSG_DESCRIBE`, `disk::MSG_DESIGNATE`), and the block service's
  badge gains `Tables` and `Designate` (`wire::Badge`). Neither opens a
  session, and a designation writes nothing a client chose.

nvme and usbd serve both with no code of their own beyond the hand-over:
the job reaches the device through the same `Device` every session does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r that holds the disk

A connection's badge decides what it reaches (`wire::Badge`): a partitions
grant lists and opens what it admits, as before; a tables badge lists every
partition and has its driver describe a disk; a designation badge does the
one designation it was minted for, on the one disk carrying its partition,
and nothing else. Neither of the new two opens a session. The supervisor
names the partitions the running system is on (`--active`), and the driver
refuses to designate one of them.

The client's side is `diskserver::tables`: `describe` and `designate`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the signed image grants

`/system/bin/disks list` describes every disk the block service serves: its
size, LBA 0, each copy of its table and every partition of the copy in
force. `disks designate data [--commit]` has the machine's one DATA
partition designated by the driver of its disk. Neither is authority of the
program's own: each needs the authorization its row was minted
(`toyos_manifest::authorize`), which the supervisor mints at every start of
the row as a connector to the block service carrying that one badge.

The build lets an authorization that changes a disk go only to a row no
login can start, and no image this repository ships carries one: today only
a signed test image's job designates. Interactive use waits for the
administrative authorization every system change will share, and nothing
disk-specific stands in for it (`issues/toyos-manages-its-own-partitions.md`).

The designation is resolved when it is asked for, not when the row starts:
the block service finds the one DATA partition among every disk it serves,
refusing none, two, or a GUID two disks carry, and the driver of that disk
judges it against its own table, its holds and the running system's
partitions, which the supervisor names (`--active`), in the turn it begins.
An image's preauthorization names a role, not a state a person reviewed, so
there is nothing for a token to bind and none is carried; a plan-bound
authorization brings one with the table edits it permits.

A foreign DATA partition is served absent rather than from memory, which
`issues/a-foreign-data-partition-is-answered-with-memory.md` asked for:
`hostdisk::left_alone` reads the absent line and that the in-memory line is
never said. The scratch disk `--adopt-scratch` writes is recorded as not
served (`issues/the-scratch-disk-adopt-scratch-makes-is-not-served.md`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e T14's row

`tests/images/designatecase.toml` is the one image whose runner holds
`designate:data`, so the authorization reaches no boot but the one that is
for it, and its runner starts nothing a login could reach.

- `the_data_partition_beside_the_stick_is_designated` (QEMU x86-64,
  `Profile::MetalBesideAHostDisk`): a judge-only `disks designate data`
  writes nothing, the committed one is written by the NVMe driver and read
  back, and after the stop the host reads the disk image: DATA begins with
  `bcachefs::designation` for its size and every other byte is as it was.
  The supervisor minting the authorization, the block service resolving and
  routing it and the driver's job are four processes and a claim no host
  build runs together.
- `bench_data_designated` (named only, the T14): the same designation on the
  bench's own NVMe, and a second boot whose DATA file server formats what it
  stamped and serves it, never from memory. It retires the host stamp of
  #1079's `toyos-metal --designate-data`.

A metal job is a binary name and its arguments: the runner's job list takes
what its stdin path always took.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e block service's hand-over

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…g it drops, and the table module says what is true

`every_shipped_boot_config_is_covered` holds `ALL_CONFIGS` to the tree, so
`tests/images/designatecase.toml` is in it and every config gate reads it.
The driver changes no table yet, so a table is still served for the disk's
life.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The stick a bench boots carries 7507812 sectors, four past its last whole
4 KiB block, which is where UEFI puts its backup header. With the primary
header torn, the table comes from there; with the count floored back to
whole blocks, as it was, it is not read at all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…arries its arguments

Two references the writer's move and the job-list change left stale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Japabu

Japabu commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator Author

Review r1: PR #1085 at 1e670670f

I read all 11 commits, the whole diff, every changed file, and the callers: the supervisor's start path and build_namespace, the nvme and usbd serve loops, toyos_gpt and each Sectors implementer, the fileserver probe, the test-runner and the metal derivation. I also read design.md and handoff.md, and the evidence logs and controls in the scratch tree. I ran nothing.

Architecture

The ownership is right and matches the approved direction:

  • The driver that holds the disk judges and writes, through the same Device and Holds its sessions use. The check and the write happen in one single-threaded turn, under a hold that place takes before any I/O.
  • diskserver resolves the target role and routes it. The supervisor mints badges only from signed authorize rows.
  • toyos_gpt::edit replaces build/gptwrite.rs as the one writer.
  • Neither new badge reaches open: partitions(badge) turns everything except Badge::Partitions into None, so it is refused NotGranted. The client chooses only commit. The driver builds the stamp in a Region it creates itself. No client bytes reach the disk.

The writer checks out against §5.3:

  • Headers have revision 1.0 and size 92, with the CRC sealed over a zeroed field. The arrays are CRC'd.
  • The backup header is at lba_count - 1 and its array ends just before it. The protective MBR record matches §5.2.3. The gpt oracle agrees byte for byte, including on a disk the alignment does not round.
  • The step order is backup, flush, primary, flush.
  • The crash model and both order controls are sound. The no-middle-flush and primary-first logs red for the stated reasons.

The true-last-sector fix is safe for every reader:

  • nvme now uses NSZE and usbd uses READ CAPACITY's count.
  • The loader, FileSectors, ImageSectors and the test Image already reported exact counts with granularity 1.
  • The kernel does not link toyos-gpt.
  • submit_io's page count is unchanged for whole blocks.
  • Served partitions stay within the floored range, because they are whole blocks inside the usable range.

Size: about +3,000/−560. Roughly 2,000 lines are production and 1,000 are tests: 604 in test files plus about 390 in-file. The large additions (edit, designate, the wire types and diskserver routing) each serve a stated stage-1 or stage-2 purpose. gptwrite.rs is deleted.

The token departure is sound and does not widen authority. A route (a) preauthorization names a role, "the machine's one DATA partition". It does not name a state a person reviewed. Any token would be drawn by the same unattended job just before it uses it, so it would bind nothing. Against a hot-plugged stick, a token drawn at row start protects no better than the current resolution: the stick can already be present at boot. In both cases the real fences are:

  • two DATA partitions refuse the request;
  • a held partition refuses it;
  • a recognised signature refuses it.

A stick that carries a DATA partition can already have the file server mount it today, so designation gives an attacker nothing new. The authority gained is exactly "designate whatever is the one DATA partition when asked". That matches the authorization's word. The departure is correct only if the comments say so, and two still describe the token design (B1).

Findings

BLOCKER B1: false or stale comments, several about authority.

  • lib/manifest/src/authorize.rs:3-4 says the authorization is scoped "in the state it finds then". At :12-13, DesignateData is "One designation … bound to its disk's state when the row starts". Both are false. Nothing is bound at row start, and the connector designates any number of times for the row's life. These are the exact semantics the token departure changed.
  • system/diskserver/src/tables.rs:2: "what disks and the supervisor ask". The supervisor calls neither function.
  • lib/bcachefs/src/superblock.rs:10-11: the stamp is "the only thing that authorises ToyOS to destroy what is on a partition". That is no longer true: the driver now overwrites block 0 under designate:data with no stamp present. The stamp authorises the file server to format.
  • lib/gpt/src/edit.rs:291-295 says "at every instant one copy is whole" without condition. The owner asked for atomicity to be claimed honestly. The claim holds only when each sector persists whole and a completed flush is on the medium, as design.md says. State that condition here.
  • lib/blockserve/src/content.rs:7-8: "Each offset is … from the document named beside it". No document is named for swap, md, F2FS or the boot sector.
  • system/disks/src/main.rs:114: "the largest binary unit that leaves a whole number above one". The code stops below 10 of the next unit, so size(4096) is "4096 B" and not 4 KiB.
  • The crate's own record now describes only half the crate. lib/gpt/Cargo.toml:9's description still says "GPT parser that finds the partition firmware booted from". :15 says the gpt dev-dependency is "the differential oracle of tests/oracle.rs", but tests/edit.rs now uses it too. The lib/gpt/src/lib.rs:1-2 header covers only the boot-partition question, though the crate is now the one writer.

Each needs a one-line correction or deletion.

BLOCKER B2: build/image.rs:672-673, a misattached doc comment. The diff inserted random_guid beneath write_at's doc. random_guid's doc now opens "Write bytes at byte at of disk…", and write_at (:695) has lost its doc.

BLOCKER B3: build/build.rs:722-733, the gate misses login authorities. The gate refuses a disk-changing word only on rows that some row starts. The supervisor puts the authorization connector in the row's svc (build_namespace, through view). swap_namespace's comment records that std passes svc to "every program its holder spawns directly — sshserver running one the manifest does not declare". So a login = true row in [boot] start with authorize = ["designate:data"] passes the gate, and the programs an ssh login runs directly inherit the authorization. That breaks the owner's "never any-login" rule and authorize.rs:5's "No login … ever holds one". No image does this today, but this gate exists to catch exactly that authoring mistake.

  • Fix: also refuse on program.login, and add one line to a_disk_changing_authorization_goes_to_no_row_a_login_can_start.
  • Alternatively, if some other mechanism already prevents it, cite that mechanism in the gate comment.

BLOCKER B4: unreachable disk states. Known::describe (system/diskserver/src/main.rs:220) admits a disk only if self.offered holds one of its partitions. disks list (system/disks/src/main.rs:53) finds disks only through the partition listing. So any disk with no served table is never described, whether it is blank, MBR, unreadable at LBA 0, or carries two invalid copies: disks list prints "no disk is served". That leaves the following unreachable:

  • Lba0::Mbr, Lba0::Blank and Lba0::Unread, with their encodings and the "no partition table", "an MBR that is not a GPT's" and "would not read" arms;
  • Copies::mbr's non-protective states, as seen through the wire.

Either is acceptable:

  • Make those states reachable. Judge describe against the drivers' disk maps (Driver::disks), and give the Tables listing the disks as well as the partitions. Stage 3 Init needs this anyway.
  • Delete the unreachable variants until Init brings their consumer.

Settled questions

  • Designation checks. admit refuses a running job, an active partition, a GUID the table does not name or does not serve, and a held span. It does this before it creates a region. The hold covers the whole job, and finish and reset/end release it on every path. Read-back follows a real flush, or the immediate "no cache" answer. Failure, reset and dropped writes all give Device, never Written. The stamp-at-block-1 control reds both the unit test and the QEMU host oracle. A stamp for another size is refused as foreign. A note: today --active can matter only through --active-unknown, which fails closed, because the loader names no partition of DATA type. It stays as a cheap bug fence that stage 2 needs.
  • Badge routing. Verified as described above. Badge::decode and Grant::decode do not overlap (tags 1|2 against 3 and 4).
  • Foreign DATA served absent. This closes the issue's exit condition. hostdisk::left_alone asserts the absent line and that the memory line never appears. Two things for the orchestrator:
    • Any bench whose DATA is still foreign now serves /apps, /config, /home and /state absent, not only /home. The risks name only the T14. Check the AMD's p-table before its next batch.
    • The "second T14 boot could reboot mid-format" risk looks moot (inference). Supervisor calls make_session_home (system/supervisor/src/main.rs:1081-1086) before it starts the first non-storage row, and that waits on DATA's file server having probed, and therefore formatted. The PR's "nothing waits for DATA" and "the runner starts its jobs after login" are both inaccurate: this image has no login. This is a PR-description correction for the orchestrator.
  • USB. The host test drives the real msc::table path through the xHCI model, using a short READ(10) at the final partial block. Its control reds. The designation itself goes through the generic Sessions/Device path that usbd sessions already use, with the owner token passed through unchanged. I consider that enough before merge.
    • The open fact is a real stick answering a short final READ(10). read_copies now reads the backup on every bring-up, so any metal boot with the 7,507,812-sector stick attached checks it.
    • What to look for: no "two copies do not agree" line for that stick, and the backup reported valid.
    • Suggest attaching it to the T14 batch.
  • Format-cache gap. It does not belong to this PR. It is a property of the file server's existing format through its write-back cache. It is also not a record yet: a PR risk note is not an issue. Either establish by inspection that Cache writes block 0 after the bitmap, or file a defect in issues/ (owner: fileserver/bcachefs; exit: the format reaches the medium superblock-last).
  • Evidence. The QEMU run at 3757d0da1 and the metal images staged at a72018eb9 are still valid. Every later commit touches only tests, a comment in table.rs, a list in a build test, or docs. --ci host and --build-only passed at fe22e03c6, and 1e670670f is docs plus an issue line. Two description points for the orchestrator:

Suggestions (optional)

  • lib/blockserve/src/content.rs: add Linux bcachefs, whose superblock magic UUID is at 4 KiB + 24, and ZFS, whose label is at 0 and 256 KiB. A Linux bcachefs volume is the likeliest one to be confused with ToyOS's BCFS by name.
  • lib/blockserve/src/link.rs:38: size FrameRx as the largest of Session::BYTES, Designation::BYTES and 4. Today it works only because 22 ≥ 21.
  • Naming: disk::Designation (the order), designate::Designation (the steps), Designated and bcachefs::designation() meet in sessions.rs. Calling the order DesignateOrder or the steps Steps would read better.
  • system/fileserver/src/main.rs:349: "disks designate data is how it becomes ToyOS's". No shipped row can do that yet, so say "a designation".
  • system/disks/src/main.rs:213: only_a_done_or_judged_designation_succeeds checks the text, not the exit status its name claims.
  • Whitespace:
    • build/build.rs:3246 lost its indentation.
    • Doubled blank lines at loader/src/rootimage.rs:238 and build/image.rs:770 and :880, where lba_count_granularity went.
    • lib/blockring/src/wire.rs:54 wraps badly.
    • system/disks sits between logkeeper and logout in Cargo.toml.

SEND BACK

…hat is true, and no unreachable disk state

- The build refuses an authorization that changes a disk to a login
  authority too, not only to a row a login can start: a login authority's
  namespace reaches every program a login runs directly. Its test reds
  without the check.
- `Lba0` and the head's LBA-0 word go: no disk without a served table is
  described today, so no consumer reaches them. They come with `Init`.
- Comments corrected: an authorization is resolved each time it is used
  and lasts the row's life; the stamp authorises a file server's format;
  the two-copy write is whole only on a device that keeps sectors whole and
  honours its flush; `tables` is `disks`'s alone; the signature table names
  documents where they exist; `toyos-gpt` is the writer too, and is no
  kernel dependency. `random_guid` and `write_at` each have their own doc.
- The link's frame is sized for every frame the service sends.
- `bench_data_designated` runs on the AMD bench too, and on the T14 refuses
  a stick whose last sector would not read.
- `issues/a-first-format-interrupted-mid-flush-can-leave-a-superblock-over-a-bitmap-it-never-wrote.md`:
  the cache writes block 0 first, so a format is not superblock-last on the
  medium.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Japabu

Japabu commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator Author

Review r2: PR #1085 at 0343c0aa8

I read commit 0343c0aa8 (the whole r2 diff), the updated description, and the r2 evidence at ToyOSOrg/evidence@60b2ef158a/partitions/r2. I ran nothing. Settled points stay settled.

r1 blockers

  • B1: resolved. Each comment now says what is true.
    • authorize.rs: the authorization is minted at every start and resolved each time it is used. DesignateData is "Designations … for as long as the row runs".
    • tables.rs no longer mentions the supervisor.
    • The stamp's doc now says it authorises a file server to format.
    • edit.rs::steps states its two device assumptions and what happens when either is broken.
    • content.rs names a document only where one exists.
    • size()'s doc ("at least 10 of it") matches the loop.
    • lib/gpt's description, dev-dependency note and crate header now cover the writer.
  • B2: resolved. write_at has its doc back, and random_guid has only its own.
  • B3: resolved. The gate refuses a disk-changing word on a login row. The test covers a boot-started sshserver with designate:data, which is refused, and with tables, which is allowed.
    • Control: controls-gate-misses-login.patch disables only the new condition, and the test reds at build.rs:3014 (unwrap_err on Ok).
    • Exit 101, reverted.
  • B4: resolved by deletion. Lba0, its wire byte, its sessions mapping and its print arms are gone, with no remnants. The fixed "a GPT" in disks list is true: a disk is described only when it offers a partition, which takes a protective MBR and a valid copy. edit::Mbr stays because in_force uses it.

r1's suggestions were taken too: the FRAME max in link.rs, the indentation, the doubled blank lines, the member order, the wire wrap, the fileserver comment and the test name.

New record and description

  • issues/a-first-format-interrupted-mid-flush-can-leave-a-superblock-over-a-bitmap-it-never-wrote.md follows issues/README.md: an open defect with a date, owner and exit condition. Its mechanism matches system/fileserver/src/cache.rs (dirty blocks lowest first, in runs, and requests in flight together are unordered). It says honestly that it was found by inspection and no boot has shown it. The exit condition is testable on the host. It is correctly outside this PR.

  • PR description, against the r1 corrections:

    One residual note on the mid-format point, an inference only: make_session_home waits at most FILES_MS (30 s) before the boot moves on. The ordering argument holds while the first format takes less than that, and nothing measures it yet. A slow format would not pass unnoticed, because the second boot's judge requires fileserver: Data serving … — bcachefs. No change is needed.

USB short-read argument

This is a sufficient real check of the claim that remained open: a real stick answers a READ(10) of its final partial block. Here is why.

  • On a 7,507,812-sector stick, read_copies always reads LBA 7,507,811 through the partial READ(10), and the primary is valid there, since the stick boots.
  • Any failure turns the sector into None: a stall, a refusal, or delivered != len. Then read_lba returns false and the backup's state becomes Gpt(ReadFailed(7507811)). Because the primary is valid, table::say must print that state on the usbd "two copies do not agree" line.
  • So a failed short read cannot pass the judge silently. Its scan of usbd's disagreement lines for ReadFailed( cannot be vacuous for this failure.

What it does not establish is that the bytes came from the right LBA. The stick holds no backup there to compare, so that cannot be checked. The CDB's LBA and length are our arithmetic, which the host test and its control already cover. That split is appropriate.

Evidence

At 0343c0aa8:

  • --ci host: exit 0, 105 steps.
  • --build-only: exit 0.
  • The QEMU pair: exit 0.
  • bench_data_designated is staged for both benches. The staging runs' exit 2 is the harness's intended Verdict::Staged ("Not a pass: nothing was judged") and not a red.

No findings remain.

LAND

The supervisor makes the log whole before it syncs the file servers and
stops the drivers, so what either says is in no file. It left the AMD
designation's format unobservable in its own readback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…designated

`nvme_leaves_the_benchs_disk_alone` held DATA foreign on every boot, so a
bench whose DATA ToyOS has designated reds on every testcases boot: the AMD
bench's #1089 testcases boot mounted its volume. Its claim is now that
nothing on the bench's disk is touched but its ToyOS DATA partition — every
NVMe session on that partition alone — and that DATA is left alone while it
is foreign, and formatted or mounted and served as bcachefs once it is
designated, never stood in for by memory. `hostdisk::left_alone` answers
which of the three DATA was; the QEMU twin, whose disk is never designated
and whose every byte is read back, still requires it foreign.

Re-judged with it: the AMD's #1089 testcases readback and the T14's #1074
diag3 readback pass. A copy of the AMD's with an NVMe session on the host's
root partition reds, as does one whose DATA is said to be in memory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Japabu

Japabu commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator Author

Review r3: PR #1085 at 05720ad63

Scope: the change since r2's LAND. That is 05720ad63 (hostdisk::left_alone and its two callers), plus the issue file in e36420782. Evidence: ToyOSOrg/evidence@5123a0ed53/partitions/r3. I ran nothing.

Sessions outside DATA: still caught, the check is unchanged.

  • Every nvme … session N opened <guid> must name a partition the driver listed with the TOYOS_DATA type, and at least one session must exist.
  • The testcases image grants no designate:data, so the driver writes nothing of its own on that disk.
  • Writes on metal are judged as before, by the sessions and by the next host boot off that disk. QEMU's byte-for-byte compare is the direct oracle.
  • Control session-on-host-root: an added session on the Linux root (0FC63DAF…) reds "… which is no ToyOS DATA".
  • Control memory: an added in-memory line reds on the must_not_say.
  • Both controls were applied to the AMD readback, and both red for the stated reason.

A foreign DATA that gets formatted: fails, unless the file server itself claims a stamp.

  • The judge needs exactly one of the three fate lines:
    • foreign-and-absent;
    • "designates this partition for ToyOS; formatting it";
    • "mounted the DATA volume".
  • Foreign also needs "nothing will be written". Formatted or Mounted also needs Data serving … — bcachefs, and memory is refused in every case.
  • So a server that says the partition is foreign and also formats it reds on two fates.
  • An unmountable volume reds: either no fate line, or a format line with no bcachefs serving line.
  • The one way through is a file server that wrongly decides an unstamped partition is stamped. On the bench, the metal judge cannot see that, because it cannot know the disk's prior state.
  • The QEMU twin still catches that defect where it is cheapest. It now pins (4, Data::Foreign) against a disk with no stamp and compares every byte after the stop. Keeping the twin's requirement at foreign is what preserves this coverage. It should stay that way.

"Designated" comes from evidence in the log, not from assumption.

  • Formatted is chosen only from the file server's line in system/fileserver/src/data.rs:228.
  • That line is printed only after designated() has found the magic and a block count that matches the partition (data.rs:197-207). A count that does not match prints "ignoring it", which drops to foreign.
  • That is log evidence, though it comes from the component being tested. On metal the only independent oracle for the stamp is the earlier bench_data_designated boot. That is enough for this row's claim, which is that nothing outside DATA is touched.

Re-judges. The AMD readback (#1089 testcases, mounted) and the T14 diag3 readback both PASS. The QEMU twin exits 0 and --ci host exits 0 at 05720ad63.

e36420782. The new issue follows the format: an open defect with an owner and a testable exit. It records a real observability gap that the AMD run exposed. It does not affect this PR's claims.

No findings.

LAND

@Japabu

Japabu commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator Author

Orchestrator: landing at 05720ad. Reviews r1–r3 LAND. Metal: both benches designated through the ToyOS-native path (T14 and AMD bench_data_designated PASS), each next boot mounted the volume, and nvme_leaves_the_benchs_disk_alone re-judged PASS on both with two red controls. Host CI (105) and the QEMU twins are green at 05720ad.

@Japabu
Japabu marked this pull request as ready for review October 28, 2026 12:46
@Japabu
Japabu added this pull request to the merge queue Oct 28, 2026
Merged via the queue into main with commit 08ba721 Oct 28, 2026
10 checks passed
@Japabu
Japabu deleted the partitions branch October 28, 2026 13:00
Japabu added a commit that referenced this pull request Oct 28, 2026
…bench's NVMe

#1085 lets ToyOS designate its own DATA, and both benches' fourth
partitions are designated and mounted. So the follow-up staged in #1079
lands. ToyOS's persistent filesystem is DATA's /home (bcachefs crate's
format, through the DATA file server and the NVMe driver), medium nvme.
persist runs its durable rows there, then files' cached operations as
fs.disk.*, on both systems: Linux on its home, ext4 on the same NVMe. A
row whose media differ still has no verdict, and these now agree.

The osbench row refuses a boot whose DATA was not a TOYOS-DATA partition
the NVMe driver serves, mounted and not absent. It also refuses the boot
that formats a newly designated DATA as a run, stating the format's
duration. ToyOS's files group keeps /tmp alone: /home is fs.disk now. The
compare's setup reads fs.disk.served_by.

The README states the disk rows' rules:
- one NVMe for both;
- each system's own filesystem, Linux's mount options recorded;
- one durability: a sync returns after the device's cache is flushed,
  ToyOS's fsync covering its whole volume;
- one cache state: every row's files are written in its run and read
  cached on both.

#1085 already judges nvme_leaves_the_benchs_disk_alone against a
designated DATA, so that part of the staged patch is superseded and
dropped. Nothing here depends on #1095 (ToyOS booting off the internal
NVMe); the judge finds DATA by the NVMe driver's partition line, wherever
the boot came from.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant