Repository navigation
nvx-rs: lease eligible templates to clones, and retire them - #885
Open
Enrique Saurez (esaurez) wants to merge 7 commits into
Open
Enrique Saurez (esaurez) wants to merge 7 commits into
Enrique Saurez (esaurez) wants to merge 7 commits into
Conversation
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved crash-durability gaps and tombstone validation bypasses undermine the store’s documented guarantees.
4 open issues
What changed in this PR
Adds eligibility checks and persistent leases to the Rust host library’s template store, preparing it for clone restoration. This is the third stage of #839; runtime integration remains for later PRs.
Changes:
- Adds restore-window eligibility checks and host identity/time sampling on Linux and Windows.
- Adds clone bindings, lease acquisition and release, tombstones, retirement, and ordered durability steps.
- Adds concurrency and failure-recovery tests, public API documentation, and error codes.
| File | Description |
|---|---|
nvx-rs/src/lib.rs |
Documents template leases in the crate overview. |
nvx-rs/src/host/template/store/tests.rs |
Shares fixtures with lease tests. |
nvx-rs/src/host/template/store/leases/tests.rs |
Tests lease lifecycle, durability, validation, and concurrency. |
nvx-rs/src/host/template/store/leases.rs |
Implements leases, clone bindings, tombstones, and retirement. |
nvx-rs/src/host/template/store.rs |
Exports lease APIs and documents store guarantees. |
nvx-rs/src/host/template/mod.rs |
Exports eligibility and lease types. |
nvx-rs/src/host/template/eligibility/tests.rs |
Tests clock selection and restore-window boundaries. |
nvx-rs/src/host/template/eligibility.rs |
Implements host-time sampling and eligibility checks. |
nvx-rs/src/host/store/mod.rs |
Registers clone bindings as sandbox-owned files. |
nvx-rs/src/host/store/files.rs |
Adds directory synchronization. |
nvx-rs/src/host/platform/windows.rs |
Reads machine identity and interrupt time. |
nvx-rs/src/host/platform/other.rs |
Adds unsupported-platform stubs. |
nvx-rs/src/host/platform/mod.rs |
Adds host identity parsing and tests. |
nvx-rs/src/host/platform/linux.rs |
Reads machine identity and boot-time clock. |
nvx-rs/src/host/mod.rs |
Documents template leasing. |
nvx-rs/src/error.rs |
Adds eligibility, quarantine, and lease error codes. |
nvx-rs/README.md |
Documents new APIs and errors. |
nvx-rs/Cargo.toml |
Enables Windows interrupt-time API support. |
doc/usage.md |
Lists the new error codes. |
Review effort: Balanced
Enrique Saurez (esaurez)
force-pushed
the
esaurez/839-pr2-template-store-20261024
branch
from
October 24, 2026 22:20
401015b to
8718d50
Compare
Enrique Saurez (esaurez)
force-pushed
the
esaurez/839-pr3-leases-20261024
branch
from
October 24, 2026 22:20
bb8fc3d to
7c6f75b
Compare
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
Cross-filesystem crash consistency, concurrent retirement, and native Windows deletion semantics warrant final human review despite no new confirmed defects.
No open issues 🎉
2 resolved since last review
Review effort: Balanced
This was referenced Oct 25, 2026
Enrique Saurez (esaurez)
force-pushed
the
esaurez/839-pr2-template-store-20261024
branch
from
October 27, 2026 02:56
8718d50 to
95a7e95
Compare
Enrique Saurez (esaurez)
force-pushed
the
esaurez/839-pr3-leases-20261024
branch
from
October 27, 2026 02:56
780ab5f to
4a8b534
Compare
This was referenced Oct 27, 2026
A generation of a template must stay in its store while a clone may still restore it, and no clone may restore one outside the restore window of its capture's time contract, which OpenVMM refuses only once the restore has begun. Add the eligibility of a capture at a host's time, apart from its compatibility: TimeContract::eligibility measures the downtime as OpenVMM's restore does, by the host's monotonic clock within the boot of the capture and by UTC otherwise, and refuses a negative downtime, and one that would advance the clone's VM time by more than 30 days. HostTime::now samples the identities and clocks of the host as OpenVMM does. Add the leases of a store's generations. Store::acquire leases an eligible generation that no tombstone quarantines to a provisioned sandbox that records no VM, launch, or lease, in one transaction under the store's lock and then the sandbox's: it writes the lease's record in the store, and the same record as the clone binding, clone.json, beside the sandbox's launch journal, and returns the sandbox's lock, under which the caller launches the clone. A lease never expires: Store::release ends it once the sandbox records no VM and no launch, and so once the clone's VM has exited, as the sandbox's store verifies through its recorded process. Store::select picks the latest eligible generation of a key. Store::tombstone quarantines a generation, which takes no new lease while its leases keep working, and Store::retire deletes it once its leases have drained: its payload, then its entry in the index, then its tombstone, and it removes the records of leases that no clone binding names any more. Each of these transactions makes its steps durable in order, syncing directories where the platform can, so that no clone binding outlasts, through a crash of the host, the record that keeps its generation in the store, even when the store and the sandbox are on different filesystems. The sandbox store counts the clone binding among its files, so that a removal of a sandbox removes it, and three error codes name the new refusals: template-ineligible, template-tombstoned, and sandbox-leased. The documentation of foreign-files now covers the template store too. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bc335726-838c-4538-a3c4-d6791aba5101
Address the review of the leases of the template store: - Tombstones are read and checked wherever the store lists them, so that a tombstone of another store or of another format, or a link, fails a listing and a selection, as it fails an acquisition, rather than quarantining a generation. A retirement removes a tombstone only once it has checked it. - A retirement makes the absence of a clone binding durable in the directory that holds the sandbox's entry, besides the sandbox's own directory: a sandbox that was removed and provisioned again at its path is a new directory, whose entries never held the binding. - The first use of a store's directory of leases or of tombstones in a process also syncs the store's parent, so that no binding names a record of a store whose own entry a crash of the host could lose. - On Windows, a record that a transaction in another process removes as this one opens or removes it is gone, rather than an error: a file system without POSIX deletion refuses every open of a removed file while other handles, such as a scanner's, hold it. A retirement that raced releases failed so, rarely. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bc335726-838c-4538-a3c4-d6791aba5101
On Windows, a refused read or removal of a template store's record counted as a removal by another transaction if an open without access rights was refused too. Win32 opens ask for SYNCHRONIZE and FILE_READ_ATTRIBUTES even then, so a file whose access control list grants neither was refused both ways, and taken for a removed one: a retirement skipped the record of a live lease, or the binding that names it, and deleted the generation, and a tombstone that refused access quarantined nothing. platform::is_deleted now opens the file with NtOpenFile, relative to its directory, whose status tells STATUS_DELETE_PENDING from STATUS_ACCESS_DENIED, which Win32 reports alike, and a refused file counts as removed only if its deletion is pending, or it is gone. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bc335726-838c-4538-a3c4-d6791aba5101
A lease named its sandbox by the absolute path through which it was leased. That path could run through a link, or, on Unix, through a directory that a `..` component passes through. Once that link or directory went, the path named nothing, though the sandbox and its clone binding remained, so a retirement took the lease for unbound, and could delete its generation while the clone ran. And a link that changed while the sandbox was leased could have redirected the binding to another sandbox. acquire now resolves the links of the sandbox's path before it takes the sandbox's lock, and the lease, the binding, and the lock that it returns all name the sandbox by that path. The tests that build paths through links now move a sandbox's directory and link its old path after the lease. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bc335726-838c-4538-a3c4-d6791aba5101
A release took the sandbox's lock through any path. If that path ran through a link, and the link changed after the release checked that no VM runs, the release could remove another sandbox's binding, whose lock it does not hold, so a retirement could then delete the generation that the other sandbox's running clone uses. A release now refuses, before it removes anything, a lock that does not name the sandbox by its lease's path. That path has its links resolved, and acquire takes the lock through it too. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bc335726-838c-4538-a3c4-d6791aba5101
A removal of a sandbox removed its clone binding with its other files without syncing the directory. A retirement that later found the binding gone synced only the directories that still existed. If the sandbox's parent had been removed and created again, that sync missed the directory that held the old entry, so a crash of the host could bring the binding back after the retirement had deleted the generation it names. A removal now removes the binding first and syncs the sandbox's directory before anything else goes. The configuration record still goes last, so a failed sync leaves a provisioned sandbox that can be removed again. The sync also covers a release that a crash interrupted before its own sync. This costs one more directory sync per removal on Unix; on Windows syncing a directory is a no-op. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bc335726-838c-4538-a3c4-d6791aba5101
A removal checked that the store recorded no VM and no launch, and then removed the sandbox's clone binding, through the sandbox's path. A lock taken through a path with a link in it stays on the directory that the link named then. If the link changed between the checks and the unlink, the removal could remove the binding of another sandbox, whose VM may run, and a retirement could then delete the generation that its clone still needs. A removal now opens the directory once. Through that handle, it checks that the directory holds the lock file whose lock the removal holds, and refuses otherwise. It then checks that no VM and no launch is recorded, removes the binding, and syncs the directory. Unix uses fstatat, unlinkat and fsync; Windows uses native opens relative to the directory, as is_deleted already did. The rest of the removal still goes through the path, under the store's trust model. A removal also removed the binding twice: first, and again with the store's other files. On a Windows file system without POSIX deletion, a reader that holds the binding open leaves the first removal pending, so the second failed with PermissionDenied and aborted the removal. The binding is now removed once, and one whose deletion is already pending counts as removed. Such a binding lingers like a lock file until its readers close it, in the removal's scan and in Lingering::wait. Where the file system supports POSIX deletion, the binding's name goes at once. The lease tests also retry the recoveries of closed launches, as the supervisor seam's tests do: a process that a concurrent test forks holds a copy of the closed log until it executes its program. Per removal, this adds a check of the lock file's identity, two stat calls, and on Windows one open of the directory; Unix already opened the directory to sync it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bc335726-838c-4538-a3c4-d6791aba5101
Enrique Saurez (esaurez)
force-pushed
the
esaurez/839-pr2-template-store-20261024
branch
from
October 27, 2026 04:39
95a7e95 to
867090f
Compare
Enrique Saurez (esaurez)
force-pushed
the
esaurez/839-pr3-leases-20261024
branch
from
October 27, 2026 04:39
4a8b534 to
c2aa75a
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Stacked on #881; this PR retargets to dev when the pull requests below it merge. The chain on
devis #883 → #876 → #871 → #873 → #874 → #879 → #881 → #885 → #886, after #863, #864, #865, #866, #867, #868, #869, #870, #872, #875, #878, and #882, which have merged.A clone restores a generation of a template from the store's payload, so the generation must stay in its store as long as a clone may still restore it, or run from it. And a clone must not restore a capture outside the restore window of its time contract, which OpenVMM refuses only once the restore has begun. This adds both to
nvx::host::template: the eligibility of a capture at a host's time, apart from its compatibility, and the leases of a store's generations, with their clone bindings, tombstones, and retirement. It changes no behavior: nothing leases a generation yet. Restore, #844, builds on it.Part of #839: the third of its four pull requests. The first, #879, adds the compatibility key, and the second, #881, the store; the fourth adds the supervisor seam and the lock order.
What changed
Eligibility (
template/eligibility.rs).TimeContract::eligibility(&HostTime), andGeneration::eligibility, decide whether a clone may restore a capture at a host's time, as OpenVMM's restore decides it (vmm_core/virt/src/time_abi/downtime.rs,select_downtime, andopenvmm_defs/src/time_abi.rs,vm_time_downtime_ns):Eligibility::Early), as on a host whose UTC is behind the capturing host's;MAX_DOWNTIME_NS, 30 days (Eligibility::Expired).HostTimeholds the host's identity, its boot's, its monotonic clock and that clock's reading, and its UTC.HostTime::nowsamples them as OpenVMM samples them at a capture (vmm_core/virt/src/time_abi/host.rs): the machine ID or machine GUID, the boot ID, and UTC between two readings ofCLOCK_BOOTTIMEor the interrupt time, whose midpoint it keeps. Its fields are public, so that callers can check eligibility at other times, such as with a margin before a window closes.Leases (
template/store/leases.rs).Store::acquireleases a generation to the clone of one sandbox, in one transaction under the store's lock and then the sandbox's lifecycle lock. It checks that the store indexes the generation, that no tombstone quarantines it, that it is eligible at the host's time, and that the sandbox is provisioned, records no VM and no launch, and holds no lease. It then writes the lease's record in the store,leases/<L>.json, and the same record as the sandbox's clone binding,clone.json, beside its launch journal, before any VMM of the clone exists. It resolves the links of the sandbox's path before it locks the sandbox, and the lease, the binding, and the returned lock all name the sandbox by that path. So the removal or change of a link, or of a directory that a..component passed through, can neither hide a live binding from a retirement nor redirect it. It returns the lease with the sandbox's lock, under which the caller launches the clone, and releases the store's lock.A lease never expires.
Store::releaseends it only once the sandbox records no VM and no launch, so once the clone's VM has exited, as the sandbox's store verifies through the VM's recordedProcessRef, also after a reattach. It removes the binding, and then the store's record. The caller takes the sandbox's lock through the lease's path,Lease::sandbox, asacquiredoes: a release refuses any other lock before it removes anything, since a link in the lock's path could change and redirect the removal to another sandbox.Store::selectselects the latest generation of a key that is neither quarantined nor ineligible, andStore::leaseslists a generation's leases.Lease::templatereturns theTemplateReferencethat a clone's activation record holds.Tombstones and retirement.
Store::tombstonequarantines a generation,tombstones/<G>.json: it takes no new lease, and selection skips it, while its leases keep working.Store::retiretombstones a generation, and deletes it once its leases have drained: its payload, then its entry in the index, then its tombstone, so that an interrupted retirement leaves a quarantined generation, which retiring it again deletes. It also removes the records of leases that no clone binding names any more, which an interrupted release or a removal of a sandbox leaves, and the temporary files of interrupted record replacements. The store's records and directories have the checks of #881's: private to the current user, no links or reparse points, and records inSTORE_FORMAT1, refused in any other. Every reader of a tombstone checks it, so a tombstone of another store or of another format fails a listing, a selection, an acquisition, or a retirement, rather than quarantining a generation or being removed. An entry that is not the store's fails withForeignFiles.Durability. Each transaction makes its steps durable in order, so that the order holds through a crash of the host, even when a sandbox and the store are on different filesystems:
acquiremakes the lease's record durable before it writes the binding, and the binding durable before it returns. If it cannot make the binding's removal durable after a failure, it keeps the record, and its error says that the sandbox may still hold the lease;releasemakes the binding's removal durable before it removes the record;LockedStore::remove, removes its binding first, and makes the binding's absence durable before anything else goes. Its configuration record still goes last, so a removal that fails to sync leaves a provisioned sandbox, which can be removed again;retiremakes the tombstone durable before it deletes anything. It makes a binding's absence durable before it removes the record of that binding's lease, since a release that crashed or failed its sync may not have: in the sandbox's directory, and in the directory that holds the sandbox's entry, since a sandbox that was removed and provisioned again at its path is a new directory. Each deletion is durable before the next, and a retirement that finds its generation already unindexed makes the index durable before it removes the tombstone.A new
sync_dir, beside the stores' atomic writes (store/files.rs), syncs a directory on Unix. A process also syncs the store's directory, and then the store's parent, the first time that it uses each ofleases/andtombstones/, which the store never removes, in case the process that created them, or the store, did not finish that sync, so that no binding names a record of a store whose own entry a crash could lose. Windows has no portable way to flush a directory, so there a rename or a removal is as durable as the filesystem makes it, as OpenVMM's snapshot publication already assumes (openvmm_helpers/src/snapshot/fs.rs,sync_directory).Concurrent removals. A release and a retirement, in other processes, may remove one lease's record at once, and the readers that take no lock may meet a record as another transaction removes it. On Windows, a file system without POSIX deletion keeps a removed file under its name while other handles, such as a scanner's, hold it, and refuses every open of it until then. Win32 reports that refusal as a denied access, as it reports a file whose access control list refuses the open. So the store takes a refused file for gone only if
platform::is_deletedconfirms it: that function opens the file again with the native API, relative to its directory, and only the statusSTATUS_DELETE_PENDING, or no file at all, counts as gone. Any other refusal fails the read or the removal, so a record that refuses access never counts as missing.A removal of a sandbox removes its binding once, and takes a binding whose deletion is pending for removed. It deletes the binding with POSIX semantics where the filesystem supports them, so that its name goes at once. Elsewhere, a binding that a reader holds lingers, as a removed lock file does, until the reader closes it: the removal leaves it, and its
Lingering::waitwaits for it, rather than reporting it as a foreign entry.The sandbox store (
store/mod.rs,store/lifecycle.rs) counts the clone binding among its files,CLONE_BINDING_NAMEinFILE_NAMES, so that a removal of a sandbox removes it, first, once, and durably, as above. The removal opens the sandbox's directory once. Through that handle, it checks that the directory holds the lock file whose lock it holds, and refuses otherwise, then that the sandbox records no VM and no launch, and then removes the binding and syncs the directory. So a link in the sandbox's path that changes meanwhile cannot redirect the binding's removal to another sandbox, whose VM may run. The rest of the removal goes through the path, as before, under the store's trust model: only the user or an administrator may change the directories above a sandbox's. The sandbox store also gainsSandboxStore::binding_path.Platform (
platform/).host_idandmonotonic_ns, on Linux and Windows, forHostTime::now.is_deleted, which on Windows tells a file whose deletion is pending from one that refuses access, throughNtOpenFile;windows-sysgains itsWdk_FoundationandWdk_Storage_FileSystemfeatures for it. AndDirectory, a directory held open for a removal's operations on its entries:fstatat,unlinkat, andfsyncon Unix, and, on Windows, native opens relative to the directory, whichis_deletednow shares, and a deletion through a handle.New public API to review
nvx::host::template:MAX_DOWNTIME_NS;HostTime;DowntimeSource;Downtime;Eligibility;TimeContract::eligibility;Generation::eligibility;LeaseId;Lease;Acquired;Retirement;LEASES_NAME;TOMBSTONES_NAME; andStore::select,acquire,release,leases,tombstone,tombstones, andretire.nvx::host::store:CLONE_BINDING_NAME, whichFILE_NAMESnow includes, andSandboxStore::binding_path.nvx::ErrorCode, the README, anddoc/usage.md:template-ineligible(kindUnavailable): the generation's restore window excludes the host's time;template-tombstoned(kindUnavailable): a tombstone quarantines the generation;sandbox-leased(kindOther): the sandbox holds a lease already.The documentation of
foreign-files, which #881 began to use for the template store, now covers it too.Assumptions to review
LockedStore::remove. For a sandbox removed otherwise, a retirement makes its binding's absence durable only while the directories that held the sandbox remain.Lease::sandboxreturns it; the lock thatacquirereturns is one.HostTime::now, and passes it toacquireandselect; OpenVMM checks the window again as it restores.acquireleaves the sandbox's lease, whichreleaseends, since the sandbox then records no launch; the fourth pull request ties a lease to the launch that it reserves.select,leases,tombstones, andLease::ofread without the store's lock, so what they return may change before the caller acts on it;acquirechecks again under the lock.Validation
At
c2aa75a, the head, restacked on #881's867090f, on Windows and on Linux x86_64:cargo fmt --check;cargo clippywith--all-targets --all-featuresand--lib --no-default-features,-D warnings;cargo testwith all, default, and no features;cargo docwith-D warnings, also of private items on Windows;cargo +1.89 check;tests/openvmm_vectors.rswith--ignored, against the OpenVMM submodule at its pin,a12d916;aci_edge_sandboxes, which builds onnvx:cargo fmt --check,cargo clippywith all features and without default features,cargo testwith all and default features, the agent's bundling,cargo doc, and its MSRV check, and, on Linux, its macOS check;nvx, against a synthetic bundle;scripts/test_nvx_tools.py's check thatdoc/usage.mddocuments every error code;cargo fmt --check, andcargo clippywith--all-targets --all-featuresand--lib --no-default-features,-D warnings, and, on Linux, the macOS check.The eligibility tests check clock fixtures within one boot and across a reboot, on another host, and by another clock, the bounds of the window, the cut of the VM's time, and the host's own time. The lease tests cover acquisitions, releases, tombstones, and retirements, their refusals, interrupted releases and retirements, records of other formats and stores, foreign entries, and links. They also check the order in which each transaction makes its steps durable, and the failed syncs, and that a removal of a sandbox makes its binding's absence durable before anything else goes, and keeps the sandbox provisioned if that sync fails. They check that a lease names its sandbox by its resolved path, also through a
..component or a link that goes later, that a link that changes during an acquisition does not redirect its binding, and that a release refuses a lock taken through a link, also once the link names another sandbox. The sandbox store's tests check that a removal through a path whose link changes between its checks and the binding's removal removes only the binding of the sandbox whose lock it holds, and that a removal through a path that has come to name another sandbox's directory refuses, and removes nothing. On Windows, they check that a removal takes a binding whose deletion is pending for removed, waits while a reader holds it, and finishes once the reader closes it. Each of these regressions fails when its fix is reverted. With several processes, they cover acquisitions that race to lease one sandbox, a tombstone that races acquisitions, and a retirement that races acquisitions and releases. On Windows, they also leave records pending deletion, as a file system without POSIX deletion does while a scanner holds them, and check that readers and a release take them for gone. They also check that records whose access control lists refuse every open are not taken for gone: a retirement then fails, and keeps the generation, and a tombstone still quarantines its generation. The race of a retirement and releases also ran 320 times on Windows, in eight parallel copies, with no failure; before the fix of concurrent removals, about one run in a hundred failed.Every check passed on both hosts. One earlier run on Linux failed
guest::cgroup::tests::settlement_collects_statuses_between_its_checks_until_the_deadline, a timing test of guest code that this change does not touch, which expects two collections within 50 ms. It passed 30 reruns on that host, and the whole suite passed again.A final review by a separate model found issues in three rounds, which
bb8fc3daddressed:Copilot's review then found four issues, and the Windows tests a race, which
7c6f75baddresses:tombstones(), and soselect(), took every entry named as a tombstone for one. The retirement of an unindexed generation also removed its tombstone without reading it. Both now check the tombstone, asacquiredoes.The review of that fix found that it also took a record whose access control list refuses every open for gone, so a retirement could delete a leased generation.
1190e11tells the two apart by the native status of the open.Copilot's second review found that a lease recorded the path through which its sandbox was leased. On Unix, that path could pass through a directory by a
..component, or, on either platform, through a link. Once that directory or link went, the path named nothing, and a retirement took the live lease for unbound.b23cb64resolves the links of the sandbox's path before it locks the sandbox. The lease, the binding, and the returned lock all name the sandbox by the resolved path, so a link that changes during or after the acquisition cannot redirect the binding either, as the final review of the fix required.Copilot's third review found that a release took the sandbox's lock through any path. If a link in that path changed after the release checked that no VM runs, the release could remove another sandbox's binding, whose lock it did not hold.
43f5272refuses, before it removes anything, a lock that does not name the sandbox by its lease's path.Copilot's fourth review found that a removal of a sandbox removed its binding without making that durable. A retirement then synced only the directories that remained, so if the sandbox's parent had been removed and created again, a crash of the host could bring the binding back after the retirement had deleted its generation.
a7e68admakes a removal remove the binding first, and sync the sandbox's directory before anything else goes. Syncing every ancestor in the retirement instead would need read access to each, which hosts with unreadable ancestors do not grant.Copilot's fifth review found two more issues in the removal of a sandbox, which
780ab5faddresses:780ab5falso makes the lease tests retry the recoveries of closed launches, as the fourth pull request's tests do: on Linux, a process that a concurrent test forks holds a copy of the closed log until it executes its program. A final review of780ab5fby a separate model found no issues.The restack on #874, through #879 and #881, resolved two conflicts, and changed no code. In the README's crate tree, it keeps
dev's text and adds this pull request's leases. In the Windows tests of the sandbox store,devadded a test of provisions that wait for a removed directory where this pull request adds its test of a binding whose deletion is pending: both tests stay, and sharedev's helper, which now also deletes directories. The race of a retirement and releases ran 320 times again at4a8b534, in eight parallel copies, with no failure. A final review of the restack by a separate model found no issues.A second restack, on #874's
fa45e48, which drops the configuration identity of activation records, through #879 and #881, replayed this pull request's commits without conflicts, and changed no code. The race of a retirement and releases ran 320 times again atc2aa75a, in eight parallel copies, with no failure. On Linux,aci_edge_sandboxes' testcrashed_vms_fall_back_to_provisioned, whose fake OpenVMM exits one second after it starts, failed once at this head while the host was loaded, because the start had not completed by then. The step passed on its rerun, and the test in 40 further runs, at this head and at #881's; this pull request adds nothing to a start's path. A final review of it by a separate model found no issues.NVX CI does not run until this pull request targets
dev.