Skip to content

nvx-rs: capture templates into their store, and publish them - #887

Open
Enrique Saurez (esaurez) wants to merge 4 commits into
esaurez/841-pr1-template-lowering-20261024from
esaurez/841-pr2-capture-publication-20261024
Open

Enrique Saurez (esaurez) wants to merge 4 commits into
esaurez/841-pr1-template-lowering-20261024from
esaurez/841-pr2-capture-publication-20261024

Conversation

@esaurez

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

Copy link
Copy Markdown
Contributor

Stacked on #884; this PR retargets when its parent merges.

Chain: #871 → #879 → #881 → #885 → #886 → #884 → this PR.

Part of #841 (PR 2 of 3: capture and publication).

Updates, as fast-forward commits on aebe922:

  1. Each capture now launches its VM from its transaction, the VM's own sandbox store, rather than from its slot. At the pinned OpenVMM, a VM that captures a snapshot must have its control socket, on Linux, in the directory that holds its --snapshot-destination, and refuses to start otherwise; the captures of nvx-rs: lower, capture and publish platform templates #841 PR 3, which run VMs through the runtime adapter, found that on Linux. The slot keeps the VMM's log and outcome report of its class's latest capture, the fake OpenVMM now enforces the rule, and the tests check it.
  2. The fake checks every control console on Unix as OpenVMM does: the socket's directory must be a directory, not a link, of the user with exactly the mode 0700, and, if the VM restores or captures a snapshot, the canonical directory that holds it, the one of --restore-snapshot first, else of --snapshot-destination.
  3. The capture tells its block devices apart by the identity that their seals record, FileSeal's device and file ID, as clones do in nvx-rs: plan the clones of templates, and check their block geometry #888. On Windows that is the volume and the 128-bit file ID of FILE_ID_INFO, where it was the volume serial number and the 64-bit file index of BY_HANDLE_FILE_INFORMATION, which Microsoft doesn't guarantee unique on ReFS, the file system of Dev Drives: there, another file at the placeholder's or the scratch's path could have passed as the one that the capture checked. Linux keeps st_dev and st_ino.

What and why

create and refresh capture a tenant-free template through the supervisor, and publish it into the template store as a new generation.

  • Store::create_template(&Capture, now: impl Fn() -> Result<HostTime>, deadline) -> Result<Created> serves the capture class with a generation that the store holds, Created::Existing: the latest that the index lists of the class, of the key of the class's latest generation, that no tombstone quarantines, and that is eligible at the host's time, as Store::select chooses among a key's generations by Generation::eligibility. Without one, it captures one, Created::Captured. It calls now once, after it holds the class's slot and has recovered it and the store's index, so a generation that a racing creation captured while it waited counts, and creations that race for a class make one generation between them. Callers pass HostTime::now, or a closure that adds a margin.
  • Store::refresh_template(&Capture, deadline) -> Result<Generation> always captures a new generation, which later selections of its key take first. Older generations and their leases are untouched.
  • Store::recover_captures(deadline) -> Result<Vec<Generation>> ends the captures that a crash interrupted, as an adapter does once as it starts.

The issue calls the first two Store::create and Store::refresh; #881's Store::create(dir) already creates the store itself, so they take a _template suffix. Capture holds a capture's inputs: its CaptureClass, the adapter's SandboxPlan, the placeholder's DistroBinding, the distro policy revision that the caller accepts, and the runtime's Artifacts.

Serialization and the transaction

Each class has a slot, captures/<the first 16 hex digits of its ClassIdentity>/: a sandbox store whose lock serializes the class's captures, and which keeps the VMM's log and outcome report of the class's latest capture. A capture works in a private transaction directory in its slot, on the store's volume, which is also the sandbox store of its VM, whose launch journal records the VMM: at the pinned OpenVMM, a VM that captures a snapshot must have its control socket, on Linux, in the directory that holds its --snapshot-destination, and a sandbox store's VM listens in the store's directory. The capture holds the transaction's lock, under its slot's, until the VM has ended, and releases it before it publishes. The capture writes capture.json (the store, the class, and the storage generation), creates the dormant scratch there as a sparse file, lowers the adapter's plan into a TemplatePlan (#884) with a fresh StorageGeneration, and cold-launches the template's VM through supervisor::launch from the transaction, as a launch of its own (LaunchId::generate, since a transaction holds no lease). It opens no application session and waits for no Ready: the guest captures itself, and OpenVMM publishes the snapshot into the transaction and exits. A template's VM never becomes a sandbox: its launch is never committed. Once the VM has ended, the capture moves the VMM's log and outcome report to the slot.

Launching a capture: block devices by path

At the pinned OpenVMM, OpenVMM opens snapshot-capable block devices itself, by path, whenever --snapshot-destination is given: without following a link (O_NOFOLLOW), and on Windows without following a reparse point (FILE_FLAG_OPEN_REPARSE_POINT), sharing only reads and deletion. A PreparedLaunch's /proc/self/fd/N paths and inherited scratch claim therefore don't serve a capture, so, as decided for #841, the capture hands OpenVMM paths, not handles:

  • Before launch, it checks the transaction, which must be private to the user; the placeholder, through its DistroBinding (identity and length), and then at its path: a regular file, not a link or other reparse point, the file that the binding's check found, owned by the user or an administrator, and that no one else may change; and the scratch at its path, the same way: the file that the capture created, of the template's length.
  • It tells files apart by the identity that their FileSeals record: the device or volume, and the file ID, on Windows the 128-bit one of FILE_ID_INFO, which ReFS keeps unique where the 64-bit file index is not.
  • As OpenVMM starts, the host holds no writable handle on either file: it closes the scratch, and keeps only the binding's read-only open of the placeholder, which on Windows denies others write access, until the VM has ended.
  • The directories above the files must be ones that only the user or an administrator may change, since OpenVMM resolves each path again. The placeholder's path is absolute, as nvx-rs: lower adapter plans into the plans of template captures #884's TemplatePlan::new requires, so the file that the host checks is the one that OpenVMM opens from the transaction, its working directory. Store's documentation states the rule, under "Block devices by path", so that a restore of a clone (nvx-rs: restore clones from a template through Activate, Ready and Start #844) takes the same one.
  • Everything else of a launch is as for a sandbox: supervisor::launch runs and supervises the process, and the devices and the NIC are rendered by SandboxPlan::openvmm_arguments, through TemplatePlan::openvmm_arguments, which is what a PreparedLaunch renders besides its handle substitution. The capture therefore builds no PreparedLaunch.

Checks after the source exits, before publication

  • The verified exit: the VM's outcome report, which the capture moved to the slot once the VM had ended, and whose predecessor it removed before the VM started, records the operation capture, the category success, and the status 0.
  • The snapshot: a private directory whose manifest TemplateManifest::read reads, whose artifacts are exactly manifest.bin, memory.bin, and state.bin, private regular files of the lengths that the manifest records, checked without reading their content, and whose key, TemplateKey::new, is its class's. A snapshot that is missing, or that lacks its manifest, fails with ErrorKind::Io, as a lost capture, not NotFound.

Publication in two commits

  1. OpenVMM publishes the snapshot into the transaction. Once the capture has checked it, it closes the transaction's lock and removes the lock's file, the scratch, and what OpenVMM left beside the snapshot (the file that backed the RAM, staging directories), and writes key.bin, durably, which marks the transaction as checked.
  2. Under the store's lock, the host renames the envelope, OpenVMM's directory with capture.json and key.bin beside it, to generations/<the next store generation> without replacing anything (renameat2 with RENAME_NOREPLACE on Linux, MoveFileExW without MOVEFILE_REPLACE_EXISTING on Windows), makes the rename durable, and commits the index with the full key, syncing the store's directory and then its parent, since Store::create syncs neither.

Only indexed generations can be acquired, and a committed payload is never rewritten. A capture that fails before its transaction is checked tears its VM down and retires the transaction once the VMM has ended; a transaction whose launch may still run stays. Recovery, by the next capture of the class or by recover_captures, ends the launch that the transaction's sandbox store records, as LockedStore::recover does, then publishes a checked transaction and retires any other, once it has moved its VMM's log and outcome report to the slot; every capture, creation or refresh, also drops the generations whose payloads a crash lost, which then take none of the store's capacity, and completes the store's interrupted publications, before it looks for a generation or counts the store's against its capacity. A creation that serves a generation that the store holds syncs the store's directory and its parent first, since the commit that listed it may have been interrupted. A payload at the index's next store generation is indexed first, once the recovery has made its rename durable as the publication would have (it syncs the payload, generations/, and the slot that the payload left), and a generation whose payload a crash lost is dropped from the index. A checked transaction is synced again before recovery publishes it, since the crash may have come before the capture synced it. recover_captures returns every generation that it published or whose publication it completed.

Lock order

A capture holds its slot's lock until its VM has ended, and takes the store's lock under it to publish. It also holds its transaction's lock, the lifecycle lock of the VM's sandbox store, until its VM has ended, and releases it before it publishes; so does a recovery, which ends the transaction's launch. The supervisor's lock order (#886) now starts with a capture slot's lock: the slot's, then the template store's, then a sandbox's or a capture transaction's, then the scratch claim. Nothing takes a slot's lock while it holds another.

Fix to #881: Store::snapshot_dir and the snapshot directories that OpenVMM creates

On Windows, OpenVMM creates the snapshot directory and each artifact with a protected access control list of their own, D:P(A;;FA;;;SY)(A;;FA;;;OW), with no entries that files inherit, and snapshot_dir refused that directory, since check_owner_only_dir requires such entries. The crate-private platform::check_snapshot_dir(path: &Path) -> io::Result<Option<String>> keeps every other rule (a plain directory, not a link or reparse point; a trusted owner; a list that is present, with only allow and deny entries, for trusted principals or the owner's aliases), and drops the inheritance rule alone: OpenVMM gives each artifact its own list, and the directory's list already lets no one else create a file in it. On Linux and other hosts it is check_owner_only_dir. Only the snapshot directory's check changes: the directories of the generations and of the payloads, and every directory that the host creates, keep check_owner_only_dir.

Other changes

  • Public constants for the layout's new entries: CAPTURES_NAME (captures), TRANSACTION_NAME (transaction), and CAPTURE_RECORD_NAME (capture.json).
  • platform::foreign_writer_problem, make_sparse, and rename_no_replace, crate-private, on Linux, Windows, and other hosts.
  • The fake OpenVMM runs the VMs of captures: it parses a capture's command line as the host renders it, opens the block devices as OpenVMM does, without following a link and, on Windows, sharing only reads and deletion, and records in its blocks dump whether a handle could write them (on Linux, through a read lease); it backs the RAM with a sparse file beside the destination, and then publishes the snapshot of a given manifest, fails, never ends, or crashes, as fake-openvmm-capture.json next to the kernel asks. On Unix, it checks where every control console listens, as OpenVMM does: in a directory, not a link, of the user with exactly the mode 0700, which holds the snapshot that the VM restores or captures, if any.
  • Docs: Store's layout and "Captures and publication", the template module, the supervisor's lock order, and nvx-rs/README.md.

Tests

With OpenVMM emulated in the test process, through hooks that fail or crash a capture at each point (src/host/template/store/capture/tests.rs):

  • captures publish generations that creations then serve, and a creation captures unless its class has an eligible generation that restores take;
  • refreshes leave older generations and their leases untouched;
  • failures around each commit: before the check (the transaction is retired; a snapshot without its manifest or its RAM is refused as Io), after the check (the transaction outlives the failure until recovery publishes it), and after the rename (the publication completes); crashes, which recovery ends; payloads lost in a crash are dropped from the index;
  • a recovery makes what it completes or publishes durable before it indexes it, and a capture makes the store's own entry durable before it reports a generation (each sync is recorded with the index at that moment); a recovery returns the generations whose publications it completed as well as its own;
  • a creation completes an interrupted publication before it looks for a generation, serves no generation whose payload a crash lost, and captures in a store whose index is full of lost generations; a recovery drops lost generations before it indexes an interrupted publication, so that a full index of them does not block it; a creation makes the store durable before it serves a generation, even when no recovery changed the index;
  • what OpenVMM leaves beside the snapshot is removed; a transaction stays while it records a launch; each VM launches from its transaction, with its control socket beside the snapshot's destination; the slot keeps the outcome report of the latest capture, and none of an earlier one;
  • links, reparse points, and unsafe ownership at the placeholder, the scratch, and the transaction are refused before the VM starts, and no handle through which the host could write a block device is open as the VM starts;
  • another file of the scratch's length, renamed over the scratch before the VM starts, is refused, and the scratch's check compares the whole of a 128-bit file ID: an ID that differs from the scratch's only above its low 64 bits, as two files' IDs on ReFS may, is refused;
  • captures of one class are serialized, and creations that race make one generation between them even when both start before the capture's anchor; recovery refuses entries of captures/ that are not slots; stores too deep for a capture's control socket are refused (Linux).
  • snapshot_dir accepts a snapshot directory with OpenVMM's exact descriptor, and still refuses a payload directory without inheritable entries (Windows).

Through supervisor::launch, with nvx-fake-openvmm (tests/template_capture.rs):

  • a creation captures, the VMM receives the template's command line and storage generation, listens and reports in the transaction (the fake refuses, on Unix, a capture whose control socket is not in the directory that holds the destination, as OpenVMM does), opens the placeholder and the scratch at their paths, and finds no handle that could write them, and a second creation serves the generation without a VM;
  • a refresh publishes a new generation and leaves the older one;
  • VMs that end without a capture (a guest failure, a VMM that fails after it published, a crash) publish nothing;
  • VMs that never end are torn down at the deadline;
  • captures of one class are serialized;
  • an adapter that crashes while its capture's VM runs leaves the VMM, which recovery ends, publishing nothing, also when the VM had published its snapshot.

The fake OpenVMM's own tests (src/testing/fake_openvmm.rs), on Unix:

  • a capture whose control socket is in its slot, beside its transaction, as at aebe922, is refused with OpenVMM's message;
  • so is a socket's directory of another mode than 0700, a link to a private directory, or a missing directory, and a control console that names no endpoint after listen=;
  • a restore listens only in the directory of the snapshot that it restores.

Validation

No VM is needed for this change: everything below ran without one, at this head.

  • Windows: in nvx-rs, with NVX_BUNDLE=skip: cargo fmt --all --check; cargo clippy --locked --all-targets --all-features -- -D warnings; cargo clippy --locked --lib --no-default-features -- -D warnings; cargo clippy --locked --all-targets --no-default-features --features <feature> -- -D warnings for host, cli, testing, serde, and bundled; cargo test --locked --all-features; cargo test --locked; cargo build --locked --lib --no-default-features and cargo test --locked --lib --no-default-features, also with --features host; RUSTDOCFLAGS="-D warnings" cargo doc --locked --no-deps --all-features; and cargo +1.89 check --locked --all-targets --all-features. Then the commands of check-aci-edge-sandboxes for aci_edge_sandboxes, those of check-nvx-rs for tests/runtime-adapter (protocol-only and host-only), and python scripts/check_markdown_links.py.
  • Linux: the same, plus --features guest; cargo check --target aarch64-apple-darwin for nvx-rs and aci_edge_sandboxes; from the root, the ignored cargo test --locked --manifest-path nvx-rs/Cargo.toml --no-default-features --test openvmm_vectors -- --ignored, which this PR does not change; python3 scripts/nvx.py check-guest-binaries; and the runtime adapter's static musl guest configuration.
  • tests/template_capture.rs passed 5 times in a row on each host. On Linux, the fake detects writers through a read lease (F_SETLEASE): on the Linux host's file system, the lease was granted with no writer, and refused with EAGAIN while a writer was open.
  • The fake's control-console checks: on Linux, at 491de89, this head's parent, with aebe922's capture.rs, whose VM listened in its slot, in place of 491de89's, all 6 tests of tests/template_capture.rs fail: the fake refuses each capture before it starts, as OpenVMM does.
  • The file IDs: with a check_scratch that compares only the low 64 bits of the file IDs, the new test of the scratch's identity fails.
  • VMs: nvx-rs: lower, capture and publish platform templates #841 PR 3, which builds on 491de89, captures templates in VMs through the runtime adapter: its 9 tests passed on Windows/WHP, and on Linux/MSHV with OpenVMM's --cpu-profile host, since that development host cannot boot its CPU's built-in profile. This head changes only how the capture identifies the files that it checks before its VM starts, which the tests above exercise on both hosts.
  • Left to CI: the full matrix, once this PR targets dev; a stacked PR runs none.

Follow-up

Older identity checks share the weakness on ReFS. They predate this stack, and this PR leaves them as they are:

  • a sandbox store's lock, whose taker checks that the path still names the file that it locked, as does its holder before it removes an unprovisioned lock file;
  • the claim of a launch's log, whose identity the launch journal records (LogClaim), and which log_claim_held checks on Windows;
  • the template store's lock (nvx-rs: add the template store #881).

They compare platform::opened_file_identity and platform::file_identity, which on Windows are the volume serial number and the 64-bit file index. A follow-up can move them to FileSeal identities; for the log's claim, that changes the journal's record.

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.

🟡 Changes recommended

Index recovery, eligibility timing for queued creations, and store-parent durability remain unresolved.

3 open issues
What changed in this PR

Adds template capture and publication to NVX’s Rust host library, building on template planning and storage to support future clone restores.

Changes:

  • Adds create, refresh, and recovery APIs with supervised capture and two-stage publication.
  • Adds platform-specific file checks, sparse-file support, and non-replacing renames.
  • Extends fake OpenVMM and tests for capture failures, recovery, concurrency, and file safety.
File Description
nvx-rs/​tests/​template_capture.rs Adds end-to-end capture and recovery tests with fake OpenVMM.
nvx-rs/​src/​testing/​fake_openvmm.rs Emulates capture launches, snapshots, and failure scenarios.
nvx-rs/​src/​host/​template/​store/​tests.rs Tests Windows snapshot directory permissions.
nvx-rs/​src/​host/​template/​store/​capture/​tests.rs Tests publication, recovery, serialization, and file checks.
nvx-rs/​src/​host/​template/​store/​capture.rs Implements capture, publication, and recovery APIs.
nvx-rs/​src/​host/​template/​store.rs Exports capture APIs and adjusts snapshot directory validation.
nvx-rs/​src/​host/​template/​mod.rs Re-exports capture types and documents their role.
nvx-rs/​src/​host/​sys/​mod.rs Registers the Linux filesystem helper.
nvx-rs/​src/​host/​sys/​fs.rs Implements Linux non-replacing rename.
nvx-rs/​src/​host/​supervisor/​mod.rs Documents capture slots in the lock order.
nvx-rs/​src/​host/​store/​files.rs Makes private file creation available within the crate.
nvx-rs/​src/​host/​platform/​windows.rs Adds snapshot permission checks, sparse files, and safe renames.
nvx-rs/​src/​host/​platform/​other.rs Adds unsupported-platform implementations.
nvx-rs/​src/​host/​platform/​linux.rs Adds capture file checks and filesystem helpers.
nvx-rs/​README.md Documents capture APIs, behavior, and testing.

Review effort: Balanced

Comment thread nvx-rs/src/host/template/store/capture.rs Outdated
Comment thread nvx-rs/src/host/template/store/capture.rs Outdated
Comment thread nvx-rs/src/host/template/store/capture.rs Outdated

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

Cross-platform crash recovery, publication durability, and file-access protections warrant final human review alongside the pending CI matrix.

No open issues 🎉

3 resolved since last review

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

Cross-platform durability and security-sensitive file handling warrant human review, and the published-snapshot recovery test needs reliable synchronization.

1 open issue

Review effort: Balanced

Comment thread nvx-rs/tests/template_capture.rs
Store::create_template serves a capture class with a generation that the
store holds and that is eligible at the host's time, which it reads once
it holds the class's slot, or captures one, and
Store::refresh_template always captures; Store::recover_captures ends the
captures that a crash interrupted.

A capture runs in its class's slot, a sandbox store under captures/ whose
lock serializes the class's captures. In a private transaction there, it
writes the capture's record, creates the dormant scratch, lowers the
adapter's plan into a TemplatePlan with a fresh storage generation, and
launches the template's VM through supervisor::launch, as a launch of its
own. OpenVMM opens snapshot-capable block devices itself, by path, without
following links and, on Windows, sharing only reads and deletion, so the
capture hands it paths: it checks the transaction, the placeholder through
its binding and at its path, and the scratch at its path, and holds no
handle that could write either file as the VM starts. Once the VMM has
exited, the capture checks the outcome report, the snapshot's manifest and
artifacts, and the key, then publishes the transaction in two commits: a
rename into generations/ that replaces nothing, then the index, whose
commit also syncs the store's own entry in its parent. Each capture, and
each recovery, first drops the generations whose payloads a crash lost,
then indexes a payload whose publication a crash interrupted, once it
has made the rename durable; a creation makes the store durable before
it serves a generation that the store holds. Older generations and their
leases stay untouched.

Store::snapshot_dir accepts the snapshot directories that OpenVMM
creates, whose own access control lists on Windows pass no entries on to
the files created in them, while every directory that the host creates
keeps its check.

The supervisor's lock order gains a capture slot's lock, which comes
before the template store's.

The fake OpenVMM runs the VMs of captures as a scenario asks, and records
whether a handle could write their block devices; the template-capture
tests run captures through the supervisor against it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 32eea669-a52c-4a53-87fa-521c498537e5

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

Cross-platform publication durability, process recovery, and Windows permission handling warrant final human review despite extensive test coverage.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

OpenVMM requires the control socket of a VM that captures a snapshot, on
Linux, in the directory of the snapshot's destination. It refused every
capture, whose VM listened in the slot of its class while the destination
is in the capture's transaction.

The capture now launches its VM from a sandbox store in the transaction
itself, whose journal records the VMM until the VM has ended. The host then
moves the VMM's log and outcome report to the slot, which keeps those of
the class's latest capture, closes the transaction's lock, and removes its
file before it checks and publishes the transaction. Recovery ends a launch
that a transaction records before it publishes or retires the transaction.

The supervisor's lock order now names the transaction's lock, which a
capture or its recovery takes under its slot's lock, and releases before
it takes the store's lock to publish.

The fake OpenVMM refuses a capture whose control socket is elsewhere, as
OpenVMM does.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 32eea669-a52c-4a53-87fa-521c498537e5

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

Cross-platform crash durability, process recovery, and access-control changes warrant final human review despite extensive test coverage.

No open issues 🎉

Review effort: Balanced

OpenVMM refuses, on Unix, a control console unless its socket's
directory is a directory, not a link, of its user with exactly the mode
0700, and, if the VM restores or captures a snapshot, the canonical
directory that holds the snapshot. The fake OpenVMM checked the second
rule for captures alone.

It now checks both rules, with OpenVMM's messages, for every control
console that it serves. As in OpenVMM, a snapshot that the VM restores
takes precedence over a capture's destination, so a restore that the
fake accepts must listen beside its snapshot too. The fake also refuses
a control console that names no endpoint after listen=.

The tests of captures' command lines use private directories. New tests
refuse other modes, a link, a missing directory, and an empty listener,
and check the rule for a restored snapshot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 32eea669-a52c-4a53-87fa-521c498537e5

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

Cross-platform publication durability, filesystem permissions, and orphan-process recovery warrant final human review despite no confirmed blocking findings.

No open issues 🎉

Review effort: Balanced

Enrique Saurez (esaurez) pushed a commit that referenced this pull request Oct 25, 2026
Merges the template mode of the runtime adapter's guest (#840 PR 2, on
#822's chain and #877) into the capture and publication of templates
(#887, on #839's stack).

The adapter host's launch keeps both sides: it reserves its launch with
a LaunchId, as the supervisor's launch now requires, and its OpenVMM
inherits the files that the prepared launch checked. The command's
closure is passed where the launch takes it, since the inherited handles
borrow the prepared launch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 32eea669-a52c-4a53-87fa-521c498537e5
A capture checks, just before its VM starts, that the placeholder at its
path is the file that its binding's check opened, and that the scratch at
its path is the file that the capture created. It compared the identities
that platform::opened_file_identity returns, which on Windows are the
volume serial number and the 64-bit file index of
BY_HANDLE_FILE_INFORMATION. That index is not guaranteed unique on ReFS,
which Dev Drives use, so another file at either path could pass as the
one that the capture checked.

Compare the identities that the files' seals record instead: the device
or volume and the file ID, which on Windows is the 128-bit ID of
FILE_ID_INFO, unique on ReFS too. Linux keeps st_dev and st_ino, and
other hosts still cannot identify files.

A test renames another file of the scratch's length over the scratch
before the VM starts, which the capture refuses, and checks that the
scratch's check compares the whole of a 128-bit file ID.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 32eea669-a52c-4a53-87fa-521c498537e5

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

Cross-platform crash consistency, process recovery, and filesystem permission enforcement warrant final human review.

No open issues 🎉

Review effort: Balanced

This branch has not been deployed

No deployments
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.

2 participants