Skip to content

Issue #29: deletion rights and native file identity

This note records the bounded non-lab closure evidence for issue #29. It applies to the exact source and test results recorded below. It does not establish Windows client/server product acceptance, actual Windows-root cleanup, or ReFS runtime acceptance; those remain TC-003/004 release gates.

PR #44 merged as 6fd8af169da631d17579a3c7eb3fa0aa8b285be0 and closed the issue. Exact reviewed head abe5f4275b2d5b9a7a510aa0759413dbcf91a727 and merged source 6fd8af169da631d17579a3c7eb3fa0aa8b285be0 passed the supported hosted runtime/artifact matrix; merged-source analyzer and 39-file documentation deployment verification also passed. The release ledger records the immutable runs, counts, digests and review boundary.

No executable runtime source changed in this packet. The base implementation already met the selected native contract; the new work supplies the missing both-command ACL, error, identity, and reconciliation evidence.

Native contract

Both temp commands discover a candidate and capture its volume serial number, 128-bit file ID, attributes, logical length, UTC last-write time, and parent identity. After ShouldProcess, the command opens the current literal path with CreateFileW using these values:

Field Value Reason
Desired access DELETE and FILE_READ_ATTRIBUTES Permit same-handle metadata validation and disposition without FILE_READ_DATA.
Share mode FILE_SHARE_READ Permit readers while preventing content/data-write, rename, or other delete opens during validation and disposition.
Creation disposition OPEN_EXISTING Never create a missing candidate.
Flags FILE_FLAG_OPEN_REPARSE_POINT Inspect the link itself and reject reparse points instead of following them.

The requested share-read/write/delete combination from the issue comment was reviewed and intentionally narrowed. Microsoft documents that FILE_SHARE_WRITE permits later write opens and FILE_SHARE_DELETE permits later delete or rename opens for the lifetime of the handle. Those operations would weaken the race boundary after identity and retention validation. The safer contract allows concurrent reads while excluding content/data writers and delete/rename opens. An existing incompatible content writer or delete/rename handle produces a sharing violation, which the owning command reports as TempFileRemovalFailed; it does not force deletion.

Windows does not apply share-mode conflicts to attribute-only access. A caller with FILE_WRITE_ATTRIBUTES can therefore change a timestamp while this handle is open. The command reads the final retention timestamp from the deletion handle immediately before setting disposition, but Windows offers no atomic check-and-delete operation for this sequence. Issue #30 owns the exact concurrent timestamp observation contract and its deterministic regressions; this share-mode decision does not claim full object or timestamp immutability.

The command reads FILE_ID_INFO, FILE_STANDARD_INFO, FILE_BASIC_INFO, and FILE_ATTRIBUTE_TAG_INFO from that opened handle. It compares volume serial number plus all 16 file-ID bytes with the discovery snapshot, rejects a directory, reparse point, different identity, or now-recent file, and only then calls SetFileInformationByHandle(FileDispositionInfo). It never reopens the path between validation and disposition. Missing-candidate errors, including native codes 2, 3, 53, and 123, are intentionally reconciled as skips. Other native open, identity, metadata, or disposition failures are reported through the command error stream.

Microsoft's Win32 documentation defines FILE_ID_INFO as a volume serial number plus a 128-bit file ID and directs callers to compare both values to determine whether two handles represent the same file. The SetFileInformationByHandle documentation requires DELETE access for FileDispositionInfo and lists ReFS as supported. The CreateFile documentation defines the share flags, OPEN_EXISTING, and FILE_FLAG_OPEN_REPARSE_POINT, and also lists ReFS as supported.

Deterministic acceptance mapping

src/Tests/Unit/TempDeletionRights.Tests.ps1 runs each command against its own isolated fixture beneath Pester's temporary directory:

Issue requirement Evidence
Delete an old candidate when content reads are denied An explicit current-token deny ACE for ReadData and allow ACE for Delete are verified; a real FileStream read must throw UnauthorizedAccessException, including when the process is elevated; the native metadata/deletion handle succeeds and the command reports one removal and four reclaimed bytes.
Report denied deletion and honor -ErrorAction Stop The file denies Delete and its parent denies DeleteSubdirectoriesAndFiles, closing both Windows authorization routes. The non-terminating case reports one PermissionDenied error and reconciles failure/removal/skip/byte counters; the terminating case must throw TempFileRemovalFailed.
Preserve a directory replacement The read-denied file is removed after real discovery and replaced by a directory. The command preserves it and reports one skip.
Reconcile disappearance The read-denied file disappears after real discovery. The command reports one skip, no failure, and no reclaimed bytes.
Use a 128-bit identity and fail closed A real native identity must expose 32 hexadecimal file-ID digits. Synthetic identities prove equality compares the volume serial and every one of the 16 file-ID bytes, including each byte in the upper 64 bits; otherwise identical IDs compare unequal when any byte changes. Equal cloned arrays compare equal, while length, timestamp, and attribute differences do not change the intentionally volume-plus-file-ID equality contract. The source contract requires a 16-byte FILE_ID_INFO buffer, and an invalid handle must produce the underlying Win32Exception instead of a path or partial identity fallback.
Avoid provider path deletion The existing TempCandidateSafety.Tests.ps1 AST regression verifies that neither owning command calls Remove-Item for candidate deletion and that both use OpenForDeletion plus MarkForDeletion.

The ACL is restored before fixture cleanup whenever the denied candidate remains. Every opened stream, safe handle, Windows identity, and redirected TEMP/TMP value is disposed or restored in test cleanup. ACL discovery, mutation, and restoration use terminating errors. The positive fixture verifies its ReadData deny and Delete allow rules; the negative fixture verifies both the file Delete deny and parent DeleteSubdirectoriesAndFiles deny before invoking a cleaner, so an ACL setup failure cannot pass as command behavior.

The same-type recent/old replacement, post-preflight reparse substitution, rename/relink, and concurrent content/timestamp observation cases are owned by issue #30 rather than duplicated here. PR #42 merged them as 3d07e9ccbfd441c388b1a3a69edc92fc326a4a66 and closed issue #30. Its merged source passed build 35332020558: 333 unit plus four integration tests on each supported PS7 lane and 337 combined tests on PS5.1, with zero failures/skips/not-run. All eleven artifact wrappers, source/runtime reports and archives were independently verified. Issue #28's directory-identity packet also merged in PR #43 as 0829d076ac13095ed016c54ea96c4a8b6abd8287. This packet was rebased onto both merges before its own reviewed-head and merged-source verification.

ReFS disposition

The implementation does not use the legacy 64-bit file index. It marshals the documented 16-byte FILE_ID_128, combines it with the volume serial number, and compares every byte. GetFileInformationByHandleEx failure is not converted to a path identity or an empty result: it throws a native error, and the command preserves the candidate. Microsoft documents ReFS support for the open and same-handle disposition APIs used by this design.

The validation host exposed NTFS volumes only. The focused tests therefore establish NTFS behavior and the 128-bit/fail-closed API contract, but they do not claim an executed ReFS filesystem result. A later approved ReFS TC-003/004 lane must still record the volume/build, raw FILE_ID_INFO behavior, ACL cases, race cases, results, and recovery. No ReFS volume was provisioned or substituted for this non-lab issue packet.

Local validation

Exact clean rebased commit a1f3b2a3e3d66c331de31c7457a17e733cab3582 passed 16/16 tests: 12 deletion-rights cases plus four documentation contracts, under PowerShell 7.6.6 and Windows PowerShell 5.1.26100.9444, with pinned Pester 5.7.1 on Windows 10.0.26200.0. Both runs had zero failures, skips and not-run tests. Retained JSON/XML evidence is named issue29-rebased-ps7 and issue29-rebased-ps51. The test-file SHA-256 is d439b0d1fc4edc0ab2eb4f11fca87dd6a523c5667f6411169e2105e90581c082. These local results do not establish final-head hosted or merged-source verification; the later exact evidence is recorded above and in the release ledger.

Independent Astra high review also ran exact clean pre-rebase head 7ce8c0f6796617c47c046a4d4c90ca2ed86d4a96 in fresh processes, with the native type verified absent before Pester. The dedicated file passed 12/12 on both hosts without a parent-process native initializer. Its own BeforeAll loaded native interop. Reports are named pr44-astra-7ce8c0f-standalone-ps7 and pr44-astra-7ce8c0f-standalone-ps51; the test hash matches the rebased result.

The following checkpoints are historical. Each result applies only to its listed source and scope, not to later changes or merged-source acceptance.

Checkpoint Scope and result on both local hosts
Initial working tree based on af330858b41335366476f0b845e3f73a3c1497d1 11 focused cases passed after CRLF normalization; an earlier 95-case supporting run preceded normalization. Neither was immutable-head evidence.
Clean integration 51ef3783f8f89c15b870042579c09a6073e816f3 15/15 (11 focused plus four documentation contracts); before the all-byte equality and standalone refinements. Test hash 29fb813d9d3cccab6599fd8fcdc7bbbedbfc98700e8bc91a2d6dd455cf331764; strict Zensical 0.0.62 and PR-range whitespace checks passed.
Review working tree based on 5f93ae011e1117eb1030b814ca6e059829708f8f 12 focused cases passed with the new equality assertions; dirty-working-tree evidence, test hash 50d67f3a8cbe8e008815b02045c58183e2afaf4e505edee28cb2598884e90037.
Clean 1bd434564823640c8a2eb8e3f7c3641bce4abf89 16/16 including documentation contracts; before standalone initialization and terminating ACL refinements.
Standalone working tree based on 1bd434564823640c8a2eb8e3f7c3641bce4abf89 12/12 with the final test hash above; superseded by the independent clean 7ce8c0f results.