[PATCH v35 0/1] MR10192: kernelbase: Handle extended length path prefix in GetLongPathNameW
``` Support the \\?\ prefix by skipping it during path resolution while preserving it in the output. Added specific handling for extended UNC paths (\\?\UNC\) to mirror standard UNC behavior, ensuring these paths are treated as network shares and not local devices. Some paths that use the extended length path were filtered out as UNC paths - but not all of them are UNC. There can be UNC path that use extended length path but not all extended length paths are UNC paths. ``` Signed-off-by: Vishnunithyasoundhar S svishnunithyasoundhar@gmail.com -- v35: kernelbase: Handle extended length path prefix in GetLongPathNameW https://gitlab.winehq.org/wine/wine/-/merge_requests/10192
From: Vishnunithyasoundhar S <svishnunithyasoundhar@gmail.com> Support the \\?\ prefix by skipping it during path resolution while preserving it in the output. Added specific handling for extended UNC paths (\\?\UNC\) to mirror standard UNC behavior, ensuring these paths are treated as network shares and not local devices. Some paths that use the extended length path were filtered out as UNC paths - but not all of them are UNC. There can be UNC path that use extended length path but not all extended length paths are UNC paths. Added todo_wine test. Signed-off-by: Vishnunithyasoundhar S <svishnunithyasoundhar@gmail.com> --- dlls/kernel32/tests/path.c | 24 ++++++++++++++++++++++++ dlls/kernelbase/file.c | 15 +++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/dlls/kernel32/tests/path.c b/dlls/kernel32/tests/path.c index aa1833504d8..b64647af7dd 100644 --- a/dlls/kernel32/tests/path.c +++ b/dlls/kernel32/tests/path.c @@ -1327,6 +1327,30 @@ static void test_GetLongPathNameW(void) length = GetLongPathNameW(shortpath, NULL, 0); ok(length == expanded, "Expected %ld, got %ld\n", expanded, length); + /* Test that extended prefix is preserved in output when converting from short to long path */ + { + WCHAR short_path[MAX_PATH], long_path[MAX_PATH], ext_short_path[4 + MAX_PATH]; + DWORD short_len, long_len; + + short_path[0] = 0; + short_len = GetShortPathNameW(tempdir, short_path, ARRAY_SIZE(short_path)); + ok(short_len, "GetShortPathNameW failed: %lu\n", GetLastError()); + + lstrcpyW(ext_short_path, prefix); + lstrcatW(ext_short_path, short_path); + lstrcatW(ext_short_path, name); + lstrcatW(ext_short_path, backslash); + lstrcatW(ext_short_path, name); + + SetLastError(0xdeadbeef); + long_len = GetLongPathNameW(ext_short_path, long_path, ARRAY_SIZE(long_path)); + todo_wine + { + ok(long_len > 0, "GetLongPathNameW failed: %ld\n", GetLastError()); + ok(!wcsncmp(long_path, prefix, 4), "Expected extended prefix in output, got %s\n", wine_dbgstr_w(long_path)); + } + } + /* NULL buffer with length crashes on Windows */ if (0) GetLongPathNameW(shortpath, NULL, 20); diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 83977a2c40b..a4d85133909 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -2116,6 +2116,21 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetLongPathNameW( LPCWSTR shortpath, LPWSTR longp return 0; } + /* Handle \\?\ prefix (Extended Length Path) */ + if (shortpath[0] == '\\' && shortpath[1] == '\\' && shortpath[2] == '?' && + shortpath[3] == '\\') { + /* Filter out entended UNC */ + if (_wcsnicmp(shortpath + 4, L"UNC\\", 4) == 0) { + lstrcatW(tmplongpath, L"UNC\\"); + lp = sp = 8; + FIXME("UNC Extended pathname %s\n", debugstr_w(shortpath)); + } + else if (shortpath[4] && shortpath[5] == ':') { + tmplongpath[lp++] = shortpath[sp++]; + tmplongpath[lp++] = shortpath[sp++]; + } + } + if (shortpath[0] == '\\' && shortpath[1] == '\\') { FIXME( "UNC pathname %s\n", debugstr_w(shortpath) ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10192
On Fri Mar 20 21:44:58 2026 +0000, Vishnunithyasoundhar S wrote:
I rebased it - hopefully should have fixed the `test case dropped` that you witnessed. todo_wine-style test - I will add it. I have edited the commit subject to `kernelbase: Handle extended length path prefix in GetLongPathNameW`. I hope kernelbase is the module name. Thanks added test.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10192#note_133173
Akihiro Sagawa (@sgwaki) commented about dlls/kernel32/tests/path.c:
length = GetLongPathNameW(shortpath, NULL, 0); ok(length == expanded, "Expected %ld, got %ld\n", expanded, length);
+ /* Test that extended prefix is preserved in output when converting from short to long path */ + { + WCHAR short_path[MAX_PATH], long_path[MAX_PATH], ext_short_path[4 + MAX_PATH]; + DWORD short_len, long_len;
Instead of creating a new block to declare the variable, please declare it at the beginning of the function. Since these variable names can be a bit confusing, maybe consider renaming them to something like `shortname2`? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10192#note_133718
Akihiro Sagawa (@sgwaki) commented about dlls/kernel32/tests/path.c:
ok(length == expanded, "Expected %ld, got %ld\n", expanded, length);
+ /* Test that extended prefix is preserved in output when converting from short to long path */ + { + WCHAR short_path[MAX_PATH], long_path[MAX_PATH], ext_short_path[4 + MAX_PATH]; + DWORD short_len, long_len; + + short_path[0] = 0; + short_len = GetShortPathNameW(tempdir, short_path, ARRAY_SIZE(short_path)); + ok(short_len, "GetShortPathNameW failed: %lu\n", GetLastError()); + + lstrcpyW(ext_short_path, prefix); + lstrcatW(ext_short_path, short_path); + lstrcatW(ext_short_path, name); + lstrcatW(ext_short_path, backslash); + lstrcatW(ext_short_path, name);
Since we want to create a prefixed short path name here, I think it would be better to use `dirname` instead. `dirname` contains the long file name (see line 1280-1283). e.g. ```c length = GetShortPathNameW(dirname, shortpath2, ARRAY_SIZE(shortpath2)); ok(length, "GetShortPathNameW failed: %lu\n", GetLastError()); ok(lstrcmpiW(dirname, shortpath2) != 0, "short path name isn't created, got %s", debugstr_w(shortpath2)); ``` -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10192#note_133719
Akihiro Sagawa (@sgwaki) commented about dlls/kernel32/tests/path.c:
+ short_path[0] = 0; + short_len = GetShortPathNameW(tempdir, short_path, ARRAY_SIZE(short_path)); + ok(short_len, "GetShortPathNameW failed: %lu\n", GetLastError()); + + lstrcpyW(ext_short_path, prefix); + lstrcatW(ext_short_path, short_path); + lstrcatW(ext_short_path, name); + lstrcatW(ext_short_path, backslash); + lstrcatW(ext_short_path, name); + + SetLastError(0xdeadbeef); + long_len = GetLongPathNameW(ext_short_path, long_path, ARRAY_SIZE(long_path)); + todo_wine + { + ok(long_len > 0, "GetLongPathNameW failed: %ld\n", GetLastError()); + ok(!wcsncmp(long_path, prefix, 4), "Expected extended prefix in output, got %s\n", wine_dbgstr_w(long_path)); It would be good to test the whole path to determine whether it is a long path name or a short path name.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10192#note_133721
Akihiro Sagawa (@sgwaki) commented about dlls/kernel32/tests/path.c:
+ WCHAR short_path[MAX_PATH], long_path[MAX_PATH], ext_short_path[4 + MAX_PATH]; + DWORD short_len, long_len; + + short_path[0] = 0; + short_len = GetShortPathNameW(tempdir, short_path, ARRAY_SIZE(short_path)); + ok(short_len, "GetShortPathNameW failed: %lu\n", GetLastError()); + + lstrcpyW(ext_short_path, prefix); + lstrcatW(ext_short_path, short_path); + lstrcatW(ext_short_path, name); + lstrcatW(ext_short_path, backslash); + lstrcatW(ext_short_path, name); + + SetLastError(0xdeadbeef); + long_len = GetLongPathNameW(ext_short_path, long_path, ARRAY_SIZE(long_path)); + todo_wine The `todo_wine` macro is used for failure tests. So, either (1) use `todo_wine`s in the first commit and add new implementation and remove them in the second commit, or (2) add the implementation with the successful tests. Personally, I prefer option (1) because it makes the TODO locations explicit. Sorry for the insufficient explanation in my previous comment.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10192#note_133720
Akihiro Sagawa (@sgwaki) commented about dlls/kernelbase/file.c:
return 0; }
+ /* Handle \\?\ prefix (Extended Length Path) */ + if (shortpath[0] == '\\' && shortpath[1] == '\\' && shortpath[2] == '?' && + shortpath[3] == '\\') {
At this point, we haven't yet confirmed that `shortpath` has at least four characters, so its length should be checked before comparison. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10192#note_133722
Akihiro Sagawa (@sgwaki) commented about dlls/kernelbase/file.c:
return 0; }
+ /* Handle \\?\ prefix (Extended Length Path) */ + if (shortpath[0] == '\\' && shortpath[1] == '\\' && shortpath[2] == '?' && + shortpath[3] == '\\') { + /* Filter out entended UNC */ + if (_wcsnicmp(shortpath + 4, L"UNC\\", 4) == 0) {
`wcsnicmp()` is preferred. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10192#note_133723
It looks like your tests are failing with the current implementation. Could you take a look and update them? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10192#note_133724
participants (3)
-
Akihiro Sagawa (@sgwaki) -
Vishnunithyasoundhar S -
Vishnunithyasoundhar S (@svishnunithyasoundhar)