mount: report . and .. from windows directories (#10556)

* mount: report . and .. from windows directories

WinFsp strips the dot entries for the root itself and expects every other
directory to report them, the way a real NTFS enumeration does: its
dirctl test asserts a subdirectory's first two entries are "." and ".."
and that a hundred files enumerate as 102 entries. Dropping them
unconditionally is what fails querydir_test.

The Go test that guarded the old behaviour went with it: os.File.Readdir
filters dot entries itself, so it could never have observed either way.

* mount: give the windows dot entries their directory type

The readdir fills an attribute block only for real children, so "." and
".." arrived with a zeroed one and were reported with mode 0. Windows
refuses to enumerate a directory whose first entry is not marked as a
directory, which is the assertion querydir_test fails on with
STATUS_OBJECT_NAME_NOT_FOUND.

They now carry the type the readdir already knew. The explorer walk also
names any unexpected entry rather than only counting, so a dot entry
leaking through reads differently from a missing file.
This commit is contained in:
Chris Lu
2026-08-04 10:44:43 -07:00
committed by GitHub
parent 89ce6e175d
commit 5a5cd15054
4 changed files with 26 additions and 53 deletions
+3 -4
View File
@@ -73,10 +73,9 @@ setfileinfo_test
# Directory enumeration
# ---------------------
# Marker-based resumption and the buffer-overflow path, neither of which the
# hand-written suite reaches.
querydir_test
querydir_buffer_overflow_test
# querydir_namelen only: checkName caps at 255 bytes while WinFsp counts 255
# characters. The other two are expected to pass now that "." and ".." are
# reported for non-root directories, which is what WinFsp enumerates.
querydir_namelen_test
# Name length
-29
View File
@@ -270,35 +270,6 @@ func TestAwkwardNames(t *testing.T) {
}
}
// Windows enumerates a directory without "." and "..", and shows whatever the
// filesystem reports, so they must not reach it. os.ReadDir filters them, so
// this reads the handle the way Windows tooling does.
func TestNoDotEntriesInListing(t *testing.T) {
dir := testRoot(t)
for i := 0; i < 3; i++ {
if err := os.WriteFile(filepath.Join(dir, fmt.Sprintf("f%d", i)), []byte("x"), 0644); err != nil {
t.Fatalf("write: %v", err)
}
}
f, err := os.Open(dir)
if err != nil {
t.Fatalf("open dir: %v", err)
}
defer f.Close()
names, err := f.Readdirnames(-1)
if err != nil {
t.Fatalf("readdirnames: %v", err)
}
for _, name := range names {
if name == "." || name == ".." {
t.Errorf("listing includes %q", name)
}
}
if len(names) != 3 {
t.Fatalf("listing has %d entries (%v), want 3", len(names), names)
}
}
func TestDeepDirectoryNesting(t *testing.T) {
dir := testRoot(t)
deep := dir