Skip to content

Re-scope M39 to the namespace-route open, and record the dropped write-cache entry - #181

Merged
MikeGrier merged 2 commits into
mainfrom
mikegrier/m39-replan
Nov 5, 2026
Merged

MikeGrier merged 2 commits into
mainfrom
mikegrier/m39-replan

Conversation

@MikeGrier

Copy link
Copy Markdown
Owner

Re-scopes root M39 and records one dropped proposal. Documentation only.

  • M39 re-scoped (CHECKLIST.md, PLANS.md). The engineer, 2026-11-05: help computing flush domains comes when a file is opened through the namespace crate, where both the name and the handle are available; a consumer handing handles to the durable ioring directly manages its own domains. That replaces the free-standing locality helper. M39.1 records the decision and its two open choices; M39.2 derives a file's domains at the open, all or nothing (local disks as disk_flush_domain, the same keys a raw volume derives; a remote file keyed by server; the blind spots left to with_domains); M39.3 keeps the directory prediction as a check against what the open derives; M39.4 stays coarse. The preamble's stale reference to NR-2 now names win-volume-sys.
  • WV-D-13 (crates/win-volume-sys/DESIGN-NOTES.md): no decoded entry for a disk's write cache (StorageDeviceWriteCacheProperty). Proposed during the re-scope and dropped by the engineer, since nothing in the workspace would act on it; the limits found are recorded, and no work is scheduled.

Mike Grier and others added 2 commits November 5, 2026 13:54
WV-D-13: the assistant proposed decoding StorageDeviceWriteCacheProperty while root M39 was re-scoped, and the engineer dropped it (2026-11-05: "let's drop it. I don't see any point."). Nothing would act on it: durable-ioring flushes unconditionally and may not skip a flush on a device's unverifiable claim, a disk with no volatile cache is still a flush domain, and a target's warranty follows from how it was opened. The limits found are recorded for a later reader, and the property stays reachable through the generic storage property query. No work is scheduled.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…e-route open

The engineer, 2026-11-05: domain assistance comes when a file is opened through the namespace crate, where both the name and the handle are available; a consumer handing handles to the durable ioring directly manages its own domains. That replaces the free-standing locality helper and settles where it lives. M39.1 records the decision and its two open choices (derive at the open or on first request; what an open by identifier gets), M39.2 derives a file's domains all or nothing (name-based entries, local disks through the volume's extents as disk_flush_domain, a remote file keyed by server, the blind spots left to with_domains), M39.3 keeps the directory prediction as a check against what the open derives, and M39.4 stays coarse. The preamble's stale reference to NR-2 now names win-volume-sys.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 5, 2026 18:55
@MikeGrier
MikeGrier enabled auto-merge November 5, 2026 18:56

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

The server-only UNC key can omit related local writes from the suspect set, while the design notes misstate failed-flush semantics and provider requirements.

2 open issues
What changed in this PR

This documentation-only PR moves planned flush-domain derivation into the namespace-route open, while leaving direct-handle consumers responsible for declaring their domains.

Changes:

  • Re-scopes M39 around local and remote domain derivation, all-or-nothing results, and directory prediction checks.
  • Updates the plan index to reflect the new scope.
  • Records WV-D-13’s decision not to add a decoded write-cache property entry, including its limitations and the remaining raw-query option.
File Description
PLANS.md Aligns the M39 summary with namespace-route derivation.
crates/​win-volume-sys/​DESIGN-NOTES.md Records the dropped write-cache proposal and rationale.
CHECKLIST.md Reworks M39’s decisions, implementation tasks, and validation plan.

Review effort: Balanced

Comment thread CHECKLIST.md
Comment on lines +543 to +544
through its name. Keyed by server and not by share, since two shares on one server sit on disks this cannot see and
the coarser key is the safe one.
| <a id="wv-d-10"></a>WV-D-10 | **The dependency query is its own function, the version is an input, and names are copied out of the reply and never followed.** Implemented by `WV-2.5` (`src/storage_dependency.rs`), from `D-24` of the namespace crate's [DESIGN-NOTES.md](../windows-namespace-request-sys/DESIGN-NOTES.md), which is superseded by this row. **It is `GetStorageDependencyInformation`, not a `DeviceIoControl`**: a documented virtdisk call (`windows-sys` feature `Win32_Storage_Vhd`), so [WV-D-7](#wv-d-7)'s helper is not involved. It returns its status as the return value, not as `BOOL` followed by `GetLastError`, and the function reads it that way. **The code is carried exactly**: a handle that is not on a virtual disk gets `ERROR_VIRTDISK_NOT_VIRTUAL_DISK` (`0xC03A0015`) for `HOST_VOLUMES`, a named Win32 error whose value is 2^31 or more, and the `io::Error` carries it through `win-error-codes` ([WE-D-1](../win-error-codes/DESIGN-NOTES.md#we-d-1)). **The shape**: `storage_dependency_information(handle, flags, version)` and `storage_dependency_information_with_capacity`, which sets the size of the first buffer for a caller who would rather not rely on the regrow, which is unverified. The namespace crate's request type, its builder, `Request` and its typed `Malformed`/`Unstable` errors are not carried over, and the version is a parameter where the builder defaulted it to 2; a malformed reply and a size that kept changing are `InvalidData`. **The version is an input.** The call fails with `ERROR_INVALID_LEVEL` unless the buffer's version field is 1 or 2 when it is made, so the function sets it, and `StorageDependencyVersion::from_raw` carries any other value to the device to refuse. **The first buffer is never below the structure.** A buffer under `size_of::<STORAGE_DEPENDENCY_INFO>()` (72 bytes) is `ERROR_INVALID_PARAMETER` and writes nothing, not even the size, so the first size is raised to that. On `ERROR_INSUFFICIENT_BUFFER` or `ERROR_MORE_DATA` the call is asked again at the size it wrote, up to four attempts, and a size that is not larger than the one it asked with is refused. **Names are copied out and never followed.** A version 2 entry holds four `PWSTR`s. They are read as addresses, compared against the address of the reply, and used as offsets into it; a name outside the bytes the call said it used, or one with no terminator inside them, is `InvalidData` and is not read. The result holds owned `Wtf16String`s, so nothing a caller keeps points into a buffer that is gone, and a null pointer is `None`; the reply is read from an `io_buffers::AlignedBuf` because the structure holds pointers. This crate therefore depends on `wtf-string` and `io-buffers`. **Flag and type values are raw-carrying newtypes**: `StorageDependencyFlags` and `DependentDiskFlags` name the documented bits and keep the rest, and nothing is defaulted on a caller's behalf. **Not documented, and stated where it is claimed:** the documentation does not say the name strings lie inside the buffer the caller gives; a variable-length structure with a size parameter suggests it, and nothing here can show it. The function's behaviour does not depend on it being so, since a pointer outside the reply is refused and not dereferenced. **Measured on 2026-10-15**, Windows 11, a Hyper-V guest, not elevated, no virtual disk attached, handles opened with no access: a volume, a root directory and a disk handle each answer success with zero entries and the version echoed back, for both versions, in a 72-byte buffer; a version other than 1 or 2 is `ERROR_INVALID_LEVEL`; every buffer size below 72 that was tried (0, 8, 12, 16, 24, 40, 64, 65 and 71) is `ERROR_INVALID_PARAMETER`; `HOST_VOLUMES` is `0xC03A0015` on all four handles; and `DISK_HANDLE` on a volume is `ERROR_INVALID_PARAMETER`. **Not observed, and not deferred:** an entry. Attaching a virtual disk needs an elevated token, which this machine's account does not have, so no real entry's layout, no placement of its strings, no ancestor level, and no reply to a buffer too small for entries were seen. The function is tested against replies built from the structure's declared layout and a stand-in device that checks what it is asked, and [sabotage.json](sabotage.json) says the same. A run with an elevated token on a machine that can attach a VHD would turn each of these into an observation. |
| <a id="wv-d-11"></a>WV-D-11 | **Amended by [WV-D-12](#wv-d-12): a success is not a test of what kind of device the handle is.** **`storage_device_number` reports the device type, number and partition number as the device wrote them.** Implemented by `WV-3.1` (`src/device_number.rs`). The engineer, 2026-10-18, "build both of these now", so that a caller can tell a volume or a disk from a file, which `GetFileType` does not (it reports a disk file for all three). **Sources read that day:** the control's page (`ms.date` 08/18/2022) and the structure's (03/29/2018). **The three fields are carried unaltered.** The structure's page gives `0xFFFFFFFF` for a device number on the disks that represent the physical paths of an MPIO disk and for the partition number of a device that cannot be partitioned, so neither is turned into an `Option` or read as a count, and what a value means is the caller's, as for [WV-D-8](#wv-d-8). **No access to the handle is needed**, as for the crate's other queries ([WV-D-2](#wv-d-2)). **A device that does not answer is the device's own error**, and a success is not read as anything more than an answer ([WV-D-12](#wv-d-12)). **Observed on 2026-10-18**, Windows 11 build 26300, one disk, handles opened with no access: the system volume reported the number of a disk its extents name and a partition number other than `0xFFFFFFFF`, a disk opened by number reported that number, and an ordinary file was refused. A volume on several disks, an MPIO disk and a device that cannot be partitioned were not observed, so the values the page gives for them are from the page and are tested against replies built from the structure's layout. **No work is queued behind this.** |
| <a id="wv-d-12"></a>WV-D-12 | **A successful device control shows that the device answered it, and nothing about what kind of device the handle is or where it sits, unless the control's page says so.** The engineer, 2026-10-20, on review of PR #155, which found `storage_device_number` claiming that a success shows a volume or a disk: "we are asserting too much based on the ioctl completing successfully", and, of removing the claims, "agreed - change documentation". **The defect.** [WV-D-11](#wv-d-11) said a success shows a volume or a disk, and [WV-D-4](#wv-d-4) that a success of `volume_disk_extents` shows a volume on this machine's own disks. Neither came from a page: the `IOCTL_STORAGE_GET_DEVICE_NUMBER` pages (Win32 and WDK) and the `STORAGE_DEVICE_NUMBER` page say what the reply holds and not which devices answer, and the `IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS` page (`ms.date` 12/05/2018) says only that SMB 3.0 does not support the control and the Cluster Shared Volume File System does. Each claim was one observation, an ordinary file refused, stated as a rule. The reviewer named other storage devices, tapes among them, that can answer the storage control; a cluster volume is the case against "local". **What the crate says now.** A success is an answer, carried as the device gave it; a refusal is the device's error; the refused file is an observation and its tests are named as one. `device_type` is the device's own report of its type and is carried, not interpreted. **Rejected: telling callers to read `device_type` to classify the handle**, the reviewer's remedy, because what a caller does with a value is the caller's, and the type is the driver's claim about itself, which is not a stronger ground than the answer was. **Not decided here: how a consumer that needs to know a handle is a disk or a volume finds out.** Each documented route ends in some part of the storage stack's word: the driver's `device_type`; the disk and volume device interfaces (`SetupDiGetClassDevs` with `GUID_DEVINTERFACE_DISK` or `GUID_DEVINTERFACE_VOLUME`) matched by device number; or the opener recording what it opened. That is the consuming crate's decision. **No work is queued in this crate.** |
| <a id="wv-d-13"></a>WV-D-13 | **No entry for a disk's write cache (`StorageDeviceWriteCacheProperty`), and none is scheduled.** The engineer, 2026-11-05: "let's drop it. I don't see any point." Proposed by the assistant while root [M39](../../CHECKLIST.md) was re-scoped: the property reports whether a disk has a write cache, whether it is write-back or write-through, enabled, flushable by the host, and battery-backed. Dropped because nothing in the workspace would act on it. durable-ioring flushes unconditionally, and skipping a flush on a device's claim, which software cannot check, is what the repository's documented-contract rule rules out; a disk with no volatile cache is still a flush domain, since a failed flush there still means its writes failed; and a target's warranty class follows from how it was opened ([DI-D-60](../durable-ioring/DESIGN-NOTES.md#di-d-60)), anything stronger being the consumer's attestation ([DI-D-68](../durable-ioring/DESIGN-NOTES.md#di-d-68)). What remained was information for a consumer's own placement or a human reading diagnostics. Limits noted at the time, for a reader who comes back to it: the answer is the device's claim and not a measurement; it describes the disk Windows sees, so behind a RAID controller or a Storage Space it is the controller's or the space's answer; and whether it reflects Windows' own "turn off write-cache buffer flushing" policy was not checked. A consumer can still read it through [`query_storage_property`](src/storage_query.rs) with the raw identifier ([WV-D-9](#wv-d-9)); what is dropped is a decoded entry of this crate's own. |
@MikeGrier
MikeGrier merged commit d0b36d9 into main Nov 5, 2026
44 checks passed
@MikeGrier
MikeGrier deleted the mikegrier/m39-replan branch November 5, 2026 19:00
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