kernel32: full DOS-wildcard semantics for FindFirstFile#142
Open
Luminger wants to merge 1 commit into
Open
Conversation
Replace the ad-hoc glob matcher with an FsRtl-exact implementation of
the NT kernel's FsRtlIsNameInExpression DOS-wildcard matching.
Windows does not match filenames with a plain '*'/'?' globber: the
FindFirstFile path rewrites '*', '?' and '.' into the special DOS tokens
DOS_STAR ('<'), DOS_QM ('>') and DOS_DOT ('"') and matches with an NFA.
That is why "*.*" matches extensionless names, "*." matches *only*
extensionless names, a trailing '?' matches zero-or-one char, and a
trailing '.' is absorbed.
Real-world impact: Watcom wmake locates its makefile by enumerating the
directory with FindFirstFileA("*.*") and matching the result names.
With a dot-requiring globber, "makefile" never appeared in the listing,
so wmake failed with "No targets specified" even when a makefile was
present -- while tools that open files by name directly (the Watcom
compiler/linker) worked fine.
translateWin32Expression() + matchWin32Expression() are based on .NET's
System.IO.Enumeration.FileSystemName (MIT: TranslateWin32Expression +
MatchPattern), Microsoft's own reimplementation of the native
FsRtlIsNameInExpression, with two deliberate deviations that make the
matcher FsRtl-exact rather than .NET-exact:
* no '\' escape character (a .NET extension for FileSystemWatcher
filters; FsRtl treats '\' as a literal character);
* FsRtl empty-string semantics (both empty => match; one empty =>
no match).
containsWildcard() is widened from "*?" to the full
FsRtlIsUnicodeCharacterWild set "*?<>\"" so that patterns containing
only DOS tokens are routed to enumeration/matching instead of a direct
file lookup, as on Windows (all five are invalid in on-disk Windows
names).
Validation:
* the ReactOS kmtest FsRtlExpression table (151 expression/name pairs
with results recorded from real Windows kernels): 151/151 pass;
* exhaustive differential test against an independent char-unit
transliteration of ReactOS's FsRtlIsNameInExpressionPrivate
(ntoskrnl/fsrtl/name.c): 0 divergences over 121M generated cases
(all patterns up to length 5 over {a,b,.,*,?,<,>,",\} x all names
up to length 6 over {a,b,.});
* test_findfile gains assertions for the strict DOS quirks: "*."
matches only extensionless names, trailing DOS_QM zero-or-one,
DOS_QM never consuming a dot ("file????" vs "file????.txt"), and
raw '<' routing + final-dot rule ("make<" matches "makefile",
"fi<" does not match "file.txt").
Note: wine's own matcher does not implement the stricter DOS quirks
("*." extensionless-only, DOS_QM zero-match); wibo is intentionally
more Windows-faithful than wine here.
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.
Replaces the ad-hoc glob matcher in the FindFirstFile path with an FsRtl-exact implementation of the NT kernel's
FsRtlIsNameInExpressionDOS-wildcard matching.Why
Windows does not match filenames with a plain
*/?globber: FindFirstFile rewrites*,?and.into the DOS tokens DOS_STAR (<), DOS_QM (>) and DOS_DOT (") and matches with an NFA. That is why*.*matches extensionless names,*.matches only extensionless names, a trailing?matches zero-or-one character, and a trailing.is absorbed.Real-world impact: Watcom's
wmakelocates its makefile by enumerating the directory withFindFirstFileA("*.*"). wibo's dot-requiring globber silently dropped extensionless names, somakefilenever appeared and wmake failed with "No targets specified" — while the Watcom compiler/linker (which open files by name) worked fine, making this miserable to track down.What
translateWin32Expression()+matchWin32Expression(): based on .NET'sSystem.IO.Enumeration.FileSystemName(MIT; Microsoft's own reimplementation of the native FsRtl matcher), with the .NET-isms removed so the matcher is FsRtl-exact: no\escape (FsRtl treats\as a literal) and FsRtl empty-string semantics.containsWildcard()widened from*?to the fullFsRtlIsUnicodeCharacterWildset*?<>", so DOS-token-only patterns route to enumeration instead of a direct lookup, as on Windows.Validation
FsRtlExpressiontable (151 expression/name pairs with results recorded from real Windows kernels): 151/151 passFsRtlIsNameInExpressionPrivate: 0 divergences over 121M generated cases (all patterns ≤ 5 over{a,b,.,*,?,<,>,",\}× all names ≤ 6 over{a,b,.})test_findfilegains assertions for the strict DOS quirks:*.extensionless-only, trailing DOS_QM zero-or-one, DOS_QM never consuming a dot (file????vsfile????.txt), raw<routing + the final-dot ruleNote: wine's own matcher does not implement the stricter DOS quirks (
*.extensionless-only, DOS_QM zero-match) — wibo becomes more Windows-faithful than wine here.Related: #127 adds its own plain
*/?globber (wildcardMatchCIindll/msvc_compat_ntdll.cpp, explicitly "DOS wildcards not handled") for its NtQueryDirectoryFile path — no conflict with this PR (different file), but once this lands that enumeration could reusematchWin32Expression()and get the DOS semantics for free.