Fix async STATUS_PENDING and add WinFsp.Net integration tests#5
Merged
Fix async STATUS_PENDING and add WinFsp.Net integration tests#5
Conversation
WinFsp.Net: Modern .NET 10 binding for WinFSP (Windows File System Proxy). Two-layer API design: - Low-level: WinFspFileSystem — thin shell, user fills delegate function pointers into 64-slot interface struct, zero abstraction - High-level: IFileSystem + FileSystemHost — Dokan-style async interface, ValueTask, Memory<byte>, automatic STATUS_PENDING management Key implementation details: - All P/Invoke via LibraryImport (AOT-compatible source-gen) - Callbacks via [UnmanagedFunctionPointer] delegate + Marshal.GetFunctionPointerForDelegate<T> (AOT-safe generic version) - DLL resolution: NativeLibrary + Windows Registry fallback - UnmanagedBufferPool: NativeMemory.AlignedAlloc pooled buffers, zero GC - Per-handle CancellationToken (auto-cancelled on Cleanup) HelloFs example: Read-only FS with one file (hello.txt = "example"). Verified working in both JIT and Native AOT (2 MB binary). Design docs in docs/winfsp-net-design.md and docs/winfsp-net-redesign.md document architecture, WinFSP pitfalls, and debugging lessons learned. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace DokanNet with custom WinFsp.Net binding library - FileSystemHost high-level API with IFileSystem interface - WinFspFileSystem low-level API for direct function pointer manipulation - Full AOT-compatible P/Invoke via [LibraryImport] source generators - PinnedBufferPool with two-level cache (ThreadLocal + ConcurrentQueue) - Implement WinFspRamAdapter bridging RamFileSystem to WinFsp callbacks - Add zero-copy synchronous I/O path (NativeBufferMemory wraps kernel buffer) - Mount strategy: try Mount Manager (\.\R:) first, fallback to DefineDosDevice - Auto-configure MountUseMountmgrFromFSD registry key when running as admin - Add Setup.bat for one-time WinFsp installation and system configuration - Add BenchmarkDotNet suite (Core / OnRead / E2E layers) - Update README, CLAUDE.md, release workflow, and docs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Under kernel cache mode (FileInfoTimeout=-1), Windows cache manager calls SetFileSize to extend the file to its final size *before* issuing WriteFile calls. The old SetLength only set _length without checking capacity (sparse extension), allowing files to claim more space than the disk has. When subsequent WriteFile calls returned STATUS_DISK_FULL, the kernel cache masked the error — ReadFile served data from cache, making sha256 appear correct while native memory never received the data. Fix: PagePool gains Reserve/Unreserve for lightweight capacity accounting. SetLength extension calls Reserve() to claim capacity without allocating physical pages. Write() consumes reservations as it rents actual pages. If capacity is insufficient, SetFileSize returns STATUS_DISK_FULL before the cache manager begins writing. Also: CreateFile/OverwriteFile no longer use allocationSize to set _length. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…on tests - Fix STATUS_PENDING response handling: build fresh FspTransactRsp on stack instead of reusing dispatcher's Response pointer (which may be invalidated after returning STATUS_PENDING). Follows MEMFS async pattern. - Zero-copy async Read/Write: wrap kernel buffer directly with NativeBufferMemory instead of renting from PinnedBufferPool + memcpy. - Remove PinnedBufferPool from FileSystemHost (no longer used). - Add WinFsp.Net.Tests project with TestMemFs (lightweight in-memory IFileSystem supporting Sync/SyncCompleted/TrueAsync modes) and 30 integration tests covering all three FileSystemHost code paths via real WinFsp mounts. - Add WinFsp installation to CI workflow and include integration tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…LA+ verified), add integration tests - Replace src/WinFsp.Net/ with WinFsp.Native NuGet package (0.1.1) - Remove examples/HelloFs/, tests/WinFsp.Net.Tests/, docs/winfsp-net-*.md (moved to hooyao/winfsp-native) - Fix PagedFileContent.Write spurious DISK_FULL: Phase 2 now unreserves own reservations before RentBatch so AllocateNewPageIfUnderCapacity succeeds. Bug reproduced and fix verified with TLA+ model checker (tla/). - Add integration test suite (tests/RamDrive.IntegrationTests/): TortureTests (10 structured concurrent tests) and ChaosTests (random FS operation fuzzer with SHA256 integrity verification, configurable duration) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add UseWindowsService() + EventLog logging for dual-mode exe (console + service) - Add Inno Setup script bundling WinFsp MSI, with Full/Green/Custom install types - Update release workflow to build and publish installer alongside zip Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ArchitecturesInstallIn64BitModeOnly was removed in Inno Setup 6.4+, replaced by ArchitecturesAllowed=x64compatible. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sets MountUseMountmgrFromFSD=1 after WinFsp install so non-admin mounts are visible to all apps (Explorer, benchmark tools, etc). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Wizard page for drive letter and capacity with validation - Writes user settings to appsettings.jsonc during install - Start Menu shortcuts: Edit Configuration + Restart Service - Instructions on how to change settings after install Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
sc.exe create with Inno Setup Exec() silently fails when the install path contains spaces (Program Files). Write service registry keys directly to avoid quoting issues. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
32-bit Inno Setup Exec('sc.exe') hits WOW64 file system redirection,
calling the 32-bit sc.exe instead of the native 64-bit one. Use
{sysnative}\sc.exe to bypass this. Falls back to direct registry
writes if sc.exe still fails.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New config option (default off) creates a \Temp directory via RamFileSystem directly after mount, avoiding Windows file API timing issues. Installer adds a checkbox on the config page for this setting. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Security descriptors: - Store per-file security descriptor on FileNode - Implement GetFileSecurity/SetFileSecurity with RawSecurityDescriptor merge - Root gets default SD (O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)) - Enables Recycle Bin and proper ACL support SetLength/SetFileSize fixes: - Remove page reservation from SetLength — sparse extension only expands page table, pages allocated on demand in Write - SetAllocationSize now checks free space (best-effort TOCTOU) so Explorer reports "not enough space" before starting a copy - EnableKernelCache defaults to false (stale FreeBytes from old Reserve mechanism is now fixed, but conservative default) ChaosTests fixes: - Overwrite: SetKnown uses CAS (gen compare) to prevent stale hash - WriteSeek/Append/Truncate/Extend: call Mutated before file modification - Rename: invalidate hash to prevent stale-path race with Overwrite - Overwrite uses FileMode.Create (not Truncate) for correct sharing semantics TLA+ full-system model (RamDiskSystem.tla): - Models PagePool, sparse page tables, 3-phase write, Read, CreateFile, Extend, Truncate, Delete, SetAllocSize TOCTOU, GetVolumeInfo observer - Verified: PoolConsistent, NoPageLeak, FreeBytesAccurate, DataIntegrity, ReadConsistent, DeadFilesClean, SingleFileCap, WriteTerminates - Minimal (3p×2f): 3.16M states, 12min — all pass - Standard (4p×2f): 66.19M states, 5h — all pass (safety + liveness) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ListDirectory now returns children sorted by name (case-insensitive). Without sorting, Dictionary.Values iteration order is unstable, causing WinFsp marker-based pagination to return duplicate or missing entries when the buffer fills and ReadDirectory is called multiple times. Also update README (Quick Start, Formal Verification section) and CLAUDE.md (TLA+ download/run/modeling guide, updated config defaults). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SetLength no longer reserves pages, so FreeBytes is never polluted and kernel cache metadata stays accurate. Safe to re-enable by default (~3x read throughput). ChaosTests now assert exactly 0 integrity failures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.
Summary
Test plan