You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Derive a file's flush domains at its open (M39) - #182
Completes root M39: flush domains for files opened through the namespace route. A file win-durable-ioring opens now carries the disks of its volume as its flush domains, so failure scoping follows the storage without the consumer declaring it.
What changed
win-volume-sys (feat): storage_device_descriptor and StorageBusType, a disk's device descriptor (StorageDeviceProperty). BusTypeNvmeof (20) is named from the documentation page, since windows-sys 0.61.2 lacks it.
win-durable-ioring (feat!): a file's flush domains are derived at its open, by path or by identifier, in OpenedFile::admit (DI-D-117):
the handle's volume, then its disk extents, then disk_flush_domain for each disk, the same keys a raw volume derives;
a virtual disk is detected by its documented bus type and followed to the volume holding its backing file;
all or nothing: any failed step leaves the file unknown, the safe default;
a file on a share is unknown, by the engineer's choice (no key for a share, since a server's name is not its identity).
Breaking change.Win32FileTarget::new now reports the domains found as well as those declared, so files on one disk share fate. with_domains adds and never removes.
DirectoryDomains::predict and confirm (DI-D-118): predict a directory's domains from its own handle, then check a file's against the prediction, which catches a junction swapped between the two.
Recorded decisions:
WV-D-13: a disk's write cache gets no decoded entry.
DI-D-117's reserved opt-out: its shape is reserved and it isn't built.
Tests
Derivation: the rule is tested against an in-memory storage stack (virtual disks, nesting, a loop, the bound, a failure at each step), plus real local files.
Prediction: a directory predicts its volume's disks, a file created in it confirms, mismatches report both sides, and a step that fails is an error rather than an empty prediction.
End to end: a data file and a log file, where a failure of the data file's storage reaches the log exactly when they share a disk. The separate case only runs where the log directory is on another disk.
Existing tests: four tests that stated the old rule now state the new one. The dioring tests set a temporary file's found disk aside through a test-only seam, because they use files on one disk to stand for separate devices.
Sabotage: 7 entries in win-volume-sys, and 13 plus a control in win-durable-ioring, all behave as declared. Two older entries were repointed.
Not yet run anywhere
These need an administrator and run on CI only:
a real virtual disk's bus type and storage dependency;
a file on a formatted scratch virtual disk sharing fate with its host volume.
Both are wired into the CI step that already attaches scratch disks. The loopback-share tests run in the existing admin-share step.
The engineer, 2026-11-05: a file win-durable-ioring opens has its flush domains derived at the open, in OpenedFile::admit, for opens by path and by identifier, all or nothing. At the open because the core reads a target's domains through an accessor once when it adds the target, blocking work belongs in the open a consumer already schedules, and a raw volume already reads its extents there; an open by identifier gets all of it, since every step reads the handle. An opt-out's shape is reserved and not built: a request method taking a non_exhaustive enum whose default derives. The texts the decision amends change with the code, in M39.2.
Completed item: M39.1: Record the decision, and settle its two open choices.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Each candidate's page was read on 2026-11-05. GetDriveTypeW is not wrapped: nothing in the derivation acts on a drive's kind. GetVolumePathNameW and GetVolumeNameForVolumeMountPointW are not needed by M39.2, which reads the opened handle; they move to M39.3, whose prediction before a file exists may want them. NetDfsGetClientInfo (the plan's DfsGetClientInfo) is needed only if M39.2.3 keys a remote file by server, so it moves there. Nothing is wrapped by this item.
Completed item: M39.2.1: Wrap the name-based calls the derivation needs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… type
Found while starting M39.2.2: M39.2.4's all-or-nothing rule and the texts DI-D-117 amends cannot land apart from the first code that derives a domain, since that code is what makes the old texts false, so the two items are one. Also: the GetStorageDependencyInformation page (read 2026-11-05) names no error for a volume that is not on a virtual disk, so the measured 0xC03A0015 is not built on; a disk is followed through its dependency when its documented bus type is BusTypeFileBackedVirtual, which needs a win-volume-sys entry.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…descriptor and bus type
IOCTL_STORAGE_QUERY_PROPERTY for StorageDeviceProperty, decoded as STORAGE_DEVICE_DESCRIPTOR: the SCSI device type and modifier, removable media, command queueing, the bus type as a raw-carrying StorageBusType that names the values the STORAGE_BUS_TYPE page lists (BusTypeNvmeof, 20, from the page, since windows-sys 0.61.2 does not declare it) and keeps any other, the four optional strings as bytes, and the bus-specific bytes. A string or the bus-specific bytes outside the reply, a string starting inside the fixed fields, or one with no terminator is InvalidData. Root M39.2.2 reads the bus type to decide whether a disk is followed to the file that backs it.
Tests: a real disk, whose version is the structure's size as the page says, every disk a volume is on, the first-buffer sizes, plain and overlapped handles, a handle that is not a disk, and built replies in both directions. Seven sabotage entries, all caught.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…dle when it is opened
DI-D-117 (root M39.2.2). A file this crate opens, by path or by identifier, has its flush domains found in OpenedFile::admit: the handle's final path in GUID form names its volume, the volume's disk extents give its disks, and each disk is disk_flush_domain, the bytes a raw volume derives. A disk whose bus type is BusTypeFileBackedVirtual is followed through the volume's storage dependency to the volume holding its backing file. All or nothing: any step that fails, as it does for a file on a share, leaves none found, which is unknown and the safe default. OpenedFile::flush_domains reports them and Win32FileTarget::new carries them; with_domains adds to them and never removes one.
BREAKING CHANGE: a file target's domains were exactly what the consumer declared, and now include the disks of the file's volume. Failure scoping follows: files on one disk share fate, as their disk does.
Tests: the rule against a storage stack in memory (virtual disks, nesting, several hosts, a loop, the bound, a failure at each step), real local files, both open modes, a declaration adding to what was found, a file over the loopback share finding none, and a scratch virtual disk on CI observing the hop. Four tests that restated the old rule now state the new one; the dioring tests set a temporary file's found disk aside through a test-only seam. Nine sabotage entries and a control, two older entries repointed; all behave as declared.
Completed item: M39.2.2: A local file's domains, all or nothing, with the texts that change with them.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… DI-D-117 records the bus-type basis
API.md section 13 called deriving flush domains from Windows' storage topology a locality helper's job; it now says win-durable-ioring derives a file's from its handle when it opens it and a raw volume's from its disks, and a target made any other way declares its own. DI-D-117 records that a virtual disk is decided by its STORAGE_DEVICE_DESCRIPTOR bus type, since the storage dependency query's page names no error for a volume that is not on one.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ey is derived for it
The engineer, 2026-11-05, choosing among unknown, one key for every remote file, and a key per server. A server's name is not its identity, clustered servers share disks, a DFS link hides its target, and a share can be this machine's own disk, so any key could split two files that share fate. A consumer that knows a share's storage declares it.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…and a declaration is its whole set
DI-D-117 (root M39.2.3, the engineer's choice "a"): no key is derived for a share. Win32FileTarget's documentation says so and why, and that with none found what a consumer declares is the file's whole set; the loopback-share test now asserts that too. No behaviour changes: M39.2.2's derivation already found none for a remote handle.
Completed item: M39.2.3: A remote file's domain.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…firm a file's against the prediction
DI-D-118 (root M39.3, the engineer's choice of the directory's own handle). DirectoryDomains::predict opens the directory through windows-namespace-request-sys' OpenFile with no access and FILE_FLAG_BACKUP_SEMANTICS, reusing open_request, and runs the same derivation a file's open runs on that handle, so the prediction and a file's domains are one rule and the same bytes. A prediction is never a declaration: confirm compares a file's own domains with it and returns a DomainMismatch carrying both, which is how a junction swapped between prediction and create is noticed. A prediction that cannot be made, as for a directory on a share, is an error with its reason, never an empty set.
Tests: a directory's prediction against its volume, a file created in it confirming, two kinds of mismatch, a missing directory, a failing stack, and over the loopback share (ignored; named so the existing CI step runs it). Four sabotage entries, all caught.
Completed item: M39.3: Predict a directory's domains before a file is created, and confirm them at the open.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…cted from its own handle
The engineer, 2026-11-05, choosing the directory's own handle over resolving its name: one derivation, the same bytes, and no second statement of which volume a path is on. The probe handle is not a target, so DI-D-64's ruling on a target's open options stands. A prediction is never a declaration, a mismatch is reported with both sides, and a prediction that cannot be made is an error.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ved flush domains
Root M39.4. A data directory and a log directory, each predicted and its file confirmed, both files opened through the route with their domains derived, a write to each, and a failure imported over the data file's domains: the data write is suspect, and the log's exactly when the files share a disk, which case it was being printed (the separate case runs only where the log directory's volume is on another disk, such as a CI runner's work disk). An import stands in for a failed flush since two flushes in one seal complete in either order. On CI only, a file on a formatted scratch virtual disk shares fate with a file on the volume holding the disk's file, through the virtual-disk hop; ScratchVolume gains create_formatted, and the CI step that attaches scratch disks runs the new test.
Completed item: M39.4: Show it end to end, in the shape of the working model.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…hived M39
Root M39 is complete and archived; the decisions' links to it and their scheduled-work lines now point at the archive.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Every M39 item was already archived under its own heading; the section and its stubs leave CHECKLIST.md, and one milestone entry in COMPLETED-CHECKLIST.md says what it delivered and links its items and decisions. PLANS.md drops its M39 sentence.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Raw virtual targets omit host-disk dependencies, so failed commits no longer mark affected host-file writes suspect, violating the durability contract.
This PR derives flush domains when win-durable-ioring opens files through the namespace route, allowing failure scoping to follow their underlying storage without consumer declarations.
Changes:
Adds storage-device descriptors and bus types to win-volume-sys.
Derives file domains through volume disks and virtual-disk dependencies, with directory prediction and confirmation.
Updates tests, sabotage cases, CI coverage, and M39 documentation.
…ation
The rustdoc job denies broken intra-doc links: `derive` named both the function and the built-in attribute macro, so it now says `fn@derive`; and a link whose target was its own text is written bare.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A mismatch between two different single-disk volumes is displayed as predicted 1, found 1, so the error text hides which domains actually differ. Include the domain values rather than only their counts so a caller logging or propagating this error can identify both sides of the mismatch.
… domains
Addresses the review of PR #182.
A raw volume or raw disk on a virtual disk carried only its own disk, while a
file beside the disk's backing file carried the host's disks, so a failed
commit on the raw target did not make the file's writes suspect.
OpenedVolume::open and OpenedDisk::open now derive their domains at the open
by the walk a file's use: a volume from its own disks and its own storage
dependency, a disk through GET_STORAGE_DEPENDENCY_FLAG_DISK_HANDLE on its own
handle. All or nothing, as for a file. Windows::disks_of no longer opens an
OpenedVolume, which would now recurse.
A final path or host volume name holding a lone UTF-16 surrogate no longer
fails the derivation: only the volume prefix is decoded.
DomainMismatch's message carries both sets of domains, not their counts.
Tests: the derivation from a volume and from a disk in memory; files and
directories named with a lone surrogate; the mismatch message; and, for an
administrator on CI, a raw volume and a raw disk on a VHD carrying the host's
domains and sharing a failure with a file on the host. Sabotage: four new
entries caught; two repointed and caught.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Records the PR #182 review fix: a raw target on a virtual disk derives the host's
disks at its open, all or nothing, by the walk a file's use.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Drive-letter reassignment can produce incorrect failure domains for the retained volume handle, and the new raw-target tests panic before validating failure scoping.
…d handle
Second review round on PR #182.
- OpenedVolume::open seeds the walk from the extents its own handle reported
and asks that handle for its storage dependency, instead of re-resolving
the path, which a drive letter reassigned since the open would send to
another volume. OpenedDisk::open asks its own handle the same way. The
walk's entry for raw targets is file_domains::seeded; StorageStack loses
hosts_of_disk, and only host volumes found are opened by name.
- The raw-target working-model tests write one sector each from AlignedBuf
through an aligned instance; the Vec<u8> harness failed the unbuffered
target's sector check before any scoping assertion ran.
- host_flush_domains numbers its file with NEXT, so tests running at once
in one process do not share it.
Sabotage: two entries repointed at seeded, two added (a plain-disk target
asks its handle for hosts; a raw volume's own disks not taken from its
extents); all four caught.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…andle
Records the second review round on PR #182: a raw volume's or disk's domains
are seeded from the handle its open retains, never from its path again.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Raw volume/disk on a virtual disk lacked the host's domains: both opens now derive by the file walk; disks via GET_STORAGE_DEPENDENCY_FLAG_DISK_HANDLE.
Lone UTF-16 surrogate broke volume_prefix/host_volume: they read only the prefix of the UTF-16 path.
Raw targets' domains are seeded from the retained handle (extents + that handle's dependency query), never the path again; new file_domains::seeded, StorageStack::hosts_of_disk removed.
Raw-target working-model tests run an aligned instance with sector-sized AlignedBuf writes.
host_flush_domains file name carries the NEXT counter.
What the rounds taught: round 1's fix took the right walk but re-entered it by name, the same "re-resolve rather than use what the open holds" defect the open-time derivation exists to avoid; and its tests were written against the cached Vec harness, which no raw unbuffered target can pass, so they had only ever run on CI's admin step. Sabotage: 2 entries repointed, 2 added, all caught. Lib tests, clippy, and rustdoc with CI flags pass locally; the raw tests run on CI's admin step.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Administrator-only Windows tests remain unrun for virtual-disk detection, storage dependencies, and shared flush-domain behavior, and pending CI results still need review.
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
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.
Completes root M39: flush domains for files opened through the namespace route. A file
win-durable-ioringopens now carries the disks of its volume as its flush domains, so failure scoping follows the storage without the consumer declaring it.What changed
win-volume-sys(feat):storage_device_descriptorandStorageBusType, a disk's device descriptor (StorageDeviceProperty).BusTypeNvmeof(20) is named from the documentation page, sincewindows-sys0.61.2 lacks it.win-durable-ioring(feat!): a file's flush domains are derived at its open, by path or by identifier, inOpenedFile::admit(DI-D-117):disk_flush_domainfor each disk, the same keys a raw volume derives;Win32FileTarget::newnow reports the domains found as well as those declared, so files on one disk share fate.with_domainsadds and never removes.DirectoryDomains::predictandconfirm(DI-D-118): predict a directory's domains from its own handle, then check a file's against the prediction, which catches a junction swapped between the two.Tests
dioringtests set a temporary file's found disk aside through a test-only seam, because they use files on one disk to stand for separate devices.win-volume-sys, and 13 plus a control inwin-durable-ioring, all behave as declared. Two older entries were repointed.Not yet run anywhere
These need an administrator and run on CI only:
Both are wired into the CI step that already attaches scratch disks. The loopback-share tests run in the existing admin-share step.