Skip to content

nvx-rs: reread a clone's GPT partition table and require a fresh upper - #957

Merged
Pedro Henrique Penna (ppenna) merged 3 commits into
devfrom
esaurez/845-pr1-gpt-fresh-scratch-20261024
Oct 29, 2026
Merged

Pedro Henrique Penna (ppenna) merged 3 commits into
devfrom
esaurez/845-pr1-gpt-fresh-scratch-20261024

Conversation

@esaurez

@esaurez Enrique Saurez (esaurez) commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Part of #845 (PR 1 of 3: GPT images and the fresh-scratch check).

Stacked on #954: this PR targets its branch, esaurez/844-pr5-clones-in-vms-20261025, and is rebased and retargeted with it.

Chain: #951 → #954 → this PR → #959 → #960, on dev; #879, #881, #885, #886, #877, #932, #884, #887, #933, #880, #936, #937, #939, #940, #941, #888, #949, and #950 have merged.

What and why

A clone's activation refused a GPT image, since the kernel created the block devices of the disk's partitions when it found the template's placeholder, so they need not be the clone's. It now assembles one, and a clone's plan takes one.

nvx-rs: reread a clone's GPT partition table and require a fresh upper

  • Before the assembly, nvx::guest::clone::apply asks the kernel to reread the distro disk's partition table with BLKRRPART, once the device appeared, through a new wrapper beside BLKFLSBUF in guest::sys::ioctl. It waits for the device as the assembly does, but never past the activation's deadline. The kernel drops the placeholder's partitions and creates the clone's, with their nodes, from the same read-only bytes that the assembly then reads and checks against the record's disk GUID and first-partition bound, as at a cold boot. A reread that fails, before anything is mounted, is ApplyError::Gpt, which now carries the root::Error, with the new DeviceStep::Reread when the kernel refuses it.
  • A clone's root must start as its image alone, so its assembly takes a crate-private option that refuses an upper whose upper or work directory exists and is not empty, with the new root::Error::NotFresh, once the upper is mounted and before the overlay. The check reads the directory's status and at most one entry, and no file's content. A cold sandbox's root::assemble keeps a scratch's upper across boots, as before.

Both failures end the test adapter's clone with the existing activation status 54, whose documentation already covers them. The adapter's fake session and its README follow the new error.

nvx-rs: restore clones of GPT disks

A clone's plan refused a GPT disk as its image before any VMM started, since no clone's guest rebuilt the partitions that its kernel created for the template's placeholder. With the reread, the plan takes a GPT disk as it takes an EROFS layer:

  • The record of a GPT clone names the disk: the identity of its binding, the GUID that the adapter's plan expects of it, and the bound of its first partition, which CloneRequest gains, as first_partition_bound, beside the journal policy. An EROFS layer has no partitions, so the record of a clone of one leaves the bound out.
  • A bound of 0 is a setting that no record holds, and the plan refuses it before it opens anything, as it refuses the request's other Activate settings, with the evidence InvalidActivate. InvalidFormat now names only an image that is not of the format and identity that its binding names.
  • The plan checks a GPT disk as it checks an EROFS layer, from its metadata alone: the check of its binding, and the geometry of the template's placeholder, whatever the placeholder's format. As at a cold launch, the guest reads and checks the partition table with nvx::gpt before it assembles the root, so the host reads none of the disk, and hashes nothing.

runtime-adapter: start GPT clones, and refuse a used upper, in VMs

runtime-adapter-host start takes --first-partition-bound BYTES for a GPT disk, as launch does, and clone::Request takes the bound, 64 KiB unless given.

In vm_clone, one template, whose placeholder is a GPT disk of two layers, serves four clones at once: the EROFS image over a scratch image and in RAM, a GPT disk of one layer in RAM, and one of three layers over a scratch image, with a bound that its first partition meets exactly. The disks' layer partitions start where none of the placeholder's do, nor each other's, so a clone whose kernel kept the placeholder's partitions would fail its assembly. Each GPT clone's root stacks the layers of its own disk's table, read-only, and returns its own first partition. A disk whose first partition is one byte larger than the bound ends the clone before it is ready, as does a scratch image that an earlier clone used, whose upper is not fresh: status 8, with the record of a failure of the request, by the evidence of an unusable root, with the guest's status 54.

The GPT writer moves from vm_layered into tests/common/gpt.rs, with the small ext2 layers and the CRC, and writes disks of any layout and length. vm_layered writes the same disks as before through it.

Not in this PR

Validation

Each commit of this PR, on #954's head 21b9983, with OpenVMM at 832d5bb4, the pin of its base: fmt, Clippy with -D warnings in every configuration of the library and of the test adapter, and the check that nvx-rs/README.md gives each module a short row and a section. At this PR's head:

  • On Windows: tests/host_modules.rs; the library's host::template and host::store tests; the test adapter's protocol-only tests, and its host tests of the library, the binaries, host_session and clone_start.
  • On Linux x86_64: the aarch64-apple-darwin check; the library's guest::clone and guest::root tests; the test adapter's musl guest library tests and guest_session; and, as root, the ignored test that checks the partition-table reread against the kernel with a loop device, and, in a process of their own, the privileged tests of guest::mappings.
  • The documentation with -D warnings, the MSRV checks of the library and of the test adapter's three configurations, and the Markdown links.

At #960's head, the top of the stack, with OpenVMM at 832d5bb4:

  • On Windows and on Linux x86_64, every step of check-nvx-rs: fmt; Clippy with -D warnings with all features and on the library with and without default features; the tests with all features and with the default ones; the library without default features; the documentation with -D warnings; and the MSRV check, with the aarch64-apple-darwin check on Linux.
  • The test adapter's fmt, Clippy, build, tests and MSRV check, in its protocol-only and host configurations on both, and in its musl guest configuration on Linux.
  • On Linux, the guest binaries' check, build and tests, and the Markdown links.
  • On Linux, as root, three times each, the ignored tests of guest::mappings, of guest::clone::activation::apply, and of the partition-table reread with a loop device, each in a process of its own: guest::mappings' privileged tests can find a mount busy at their teardown when another test of the same process starts a child, as they do at runtime-adapter: start clones of templates #954's head.
  • CI at the same head, in run 40829337538: 36 jobs passed, among them the microVM tests and the platform checks on KVM, MSHV and WHP, and OpenVMM's unit and VM tests; the six that run only for a pull request or on dev were skipped.
  • The test adapter's VM suites, on Windows (WHP) and on Linux (MSHV), with OpenVMM's release build of 832d5bb4: vm_clone, 16 scenarios, with this PR's two: the GPT clones, and the used upper; vm_layered, 43; vm_restart, 3; vm_steps, 3; and vm_template, 9.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The guest-only snapshot lacks end-to-end restore coverage for the privileged partition reread, leaving #961’s GPT and used-upper VM scenarios and validation on KVM, MSHV, and WHP unconfirmed.

No open issues 🎉

What changed in this PR

This PR implements the guest-side portion of #845’s GPT clone activation: refreshing partition devices before root assembly and requiring a fresh upper, while preserving cold-boot scratch reuse. Host-side GPT support and VM scenarios remain in #961 for the final restack.

Changes:

  • Add a BLKRRPART wrapper and reread GPT partition tables before clone root assembly.
  • Reject nonempty upper or work directories during clone activation.
  • Extend tests and documentation for partition rereads, freshness checks, and activation failure status 54.
File Description
tests/​runtime-adapter/​tests/​guest_session.rs Simulate partition rereads and verify activation failures.
tests/​runtime-adapter/​src/​worker/​activation.rs Update failure documentation and GPT error tests.
tests/​runtime-adapter/​src/​worker.rs Document GPT rereads and fresh-upper requirements.
tests/​runtime-adapter/​README.md Describe updated activation behavior and failures.
nvx-rs/​src/​guest/​sys/​mod.rs Re-export the partition-table reread wrapper.
nvx-rs/​src/​guest/​sys/​ioctl.rs Add and test the BLKRRPART wrapper.
nvx-rs/​src/​guest/​root/​tests.rs Test freshness checks, reread errors, and kernel partition refresh.
nvx-rs/​src/​guest/​root/​kernel.rs Add real and fake kernel reread operations.
nvx-rs/​src/​guest/​root/​block.rs Resolve the distro device and request its partition reread.
nvx-rs/​src/​guest/​root.rs Add clone-only freshness enforcement and typed errors.
nvx-rs/​src/​guest/​clone/​activation/​apply/​tests.rs Verify GPT inputs, reread ordering, and failure handling.
nvx-rs/​src/​guest/​clone/​activation/​apply.rs Enable GPT assembly after rereading, with a fresh upper.
nvx-rs/​src/​guest/​clone.rs Document the updated clone activation contract.
nvx-rs/​README.md Update the library’s clone activation overview.

Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Exact-head GPT and reused-scratch VM results on KVM, MSHV, and WHP are still needed to validate restored-kernel partition-device replacement and devtmpfs behavior.

No open issues 🎉

Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

GPT activation relies on privileged partition-device rebuilding after snapshot restore, and pending KVM, MSHV, and WHP VM suites must confirm layout replacement and used-scratch rejection beyond the supplied loop-device validation.

No open issues 🎉

Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Exact-head KVM, MSHV, and WHP VM checks remain pending, leaving restored partition-device replacement and used-scratch rejection unverified beyond unit fakes.

No open issues 🎉

Review effort: Balanced

@esaurez
Enrique Saurez (esaurez) force-pushed the esaurez/844-pr5-clones-in-vms-20261025 branch from abe6a88 to 21b9983 Compare October 29, 2026 05:43
Enrique Saurez and others added 3 commits October 28, 2026 23:13
A clone's activation refused a GPT image, since the kernel created the
block devices of the disk's partitions when it found the template's
placeholder, so they need not be the clone's. It now assembles one:

- Before the assembly, nvx::guest::clone::apply asks the kernel to
  reread the distro disk's partition table with BLKRRPART, once the
  device appeared, through a new wrapper beside BLKFLSBUF in
  guest::sys::ioctl. The kernel drops the placeholder's partitions and
  creates the clone's, with their nodes, from the same read-only bytes
  that the assembly then reads and checks against the record's disk
  GUID and first-partition bound, as at a cold boot. A reread that
  fails, before anything is mounted, is ApplyError::Gpt, which now
  carries the root::Error, with the new DeviceStep::Reread when the
  kernel refuses it.
- A clone's root must start as its image alone, so its assembly takes
  a crate-private option that refuses an upper whose `upper` or `work`
  directory exists and is not empty, with the new root::Error::NotFresh,
  once the upper is mounted and before the overlay. The check reads the
  directory's status and at most one entry, and no file's content. A
  cold sandbox's root::assemble keeps a scratch's upper across boots, as
  before.

Both failures end the runtime adapter's clone with the existing
activation status 54, whose documentation already covers them. The
adapter's fake session and its README follow the new error.

The tests cover the reread before the assembly, its errors, and the
freshness check and its bound, without a VM, and an ignored test checks
the reread against the kernel with a loop device, as root. The VM tests
of a GPT clone come with the restore of clones in VMs.

Part of #845

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80932e09-af82-4b79-b417-70a241303cda
A clone's plan refused a GPT disk as its image before any VMM started:
the kernel of a clone's guest had created the block devices of the
partitions of the template's placeholder, and no clone rebuilt them. The
guest of a GPT clone now asks the kernel to reread the disk's partition
table before it assembles the root, so the plan takes a GPT disk as it
takes an EROFS layer.

The record of a GPT clone names the disk: the identity of its binding,
the GUID that the adapter's plan expects of it, and the bound of its
first partition, which CloneRequest gains, as first_partition_bound,
beside the journal policy. An EROFS layer has no partitions, so the
record of a clone of one leaves the bound out. A bound of 0 is a setting
that no record holds, and the plan refuses it before it opens anything,
as it refuses the request's other Activate settings, with the evidence
InvalidActivate. InvalidFormat now names only an image that is not of
the format and identity that its binding names.

The plan checks a GPT disk as it checks an EROFS layer, from its
metadata alone: the check of its binding, and the geometry of the
template's placeholder, whatever the placeholder's format. As at a cold
launch, the guest reads and checks the partition table with nvx::gpt
before it assembles the root, so the host reads none of the disk, and
hashes nothing.

The test adapter's clone requests bound a GPT disk's first partition by
64 KiB, the default of a launch's guest.

Part of #845

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80932e09-af82-4b79-b417-70a241303cda
A clone's guest rereads a GPT disk's partition table before it assembles
the root, and refuses an upper that is not fresh, and the host now plans
GPT clones. These VM tests start such clones through
runtime-adapter-host start, on the hypervisor.

start takes --first-partition-bound BYTES for a GPT disk, as launch
does, and clone::Request takes the bound, 64 KiB unless given.

In vm_clone, one template, whose placeholder is a GPT disk of two
layers, serves four clones at once: the EROFS image over a scratch image
and in RAM, a GPT disk of one layer in RAM, and one of three layers over
a scratch image, with a bound that its first partition meets exactly.
The disks' layer partitions start where none of the placeholder's do,
nor each other's, so a clone whose kernel kept the placeholder's
partitions would fail its assembly. Each GPT clone's root stacks the
layers of its own disk's table, read-only, and returns its own first
partition. A disk whose first partition is one byte larger than the
bound ends the clone with status 54, as does a scratch image that an
earlier clone used, whose upper is not fresh.

The GPT writer moves from vm_layered into tests/common/gpt.rs, with the
small ext2 layers and the CRC, and writes disks of any layout and
length. vm_layered writes the same disks as before through it.

Part of #845

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80932e09-af82-4b79-b417-70a241303cda

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

A maintainer must confirm exact-head KVM, MSHV, and WHP VM results for GPT partition replacement after snapshot restore and used-upper rejection, since source-only review cannot validate these privileged kernel behaviors.

No open issues 🎉

Review effort: Balanced

@esaurez Enrique Saurez (esaurez) removed their assignment Oct 29, 2026
Base automatically changed from esaurez/844-pr5-clones-in-vms-20261025 to dev October 29, 2026 13:40
@ppenna
Pedro Henrique Penna (ppenna) merged commit a6471c9 into dev Oct 29, 2026
3 checks passed
@ppenna
Pedro Henrique Penna (ppenna) deleted the esaurez/845-pr1-gpt-fresh-scratch-20261024 branch October 29, 2026 13:53
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.

3 participants