In CopyFileEx, and DeleteFile functions, by default, the file name and path are limited to MAX_PATH characters. To extend this limit to 32,767 wide characters, need prepend "\\?\" to the path.
-- v11: kernelbase: Limit the maximum path length for DeleteFile. kernelbase: Fix DeleteFileA doesn't support long path. kernelbase: Limit the maximum path length for filesystem. ntdll: Check if long path aware is enabled. kernel32/tests: Add tests for DeleteFile kernel32/tests: Add tests for maximum path length limitation.
From: yaoyongjie yaoyongjie@uniontech.com
In CopyFileEx, and DeleteFile functions, by default, the file name and path are limited to MAX_PATH characters. To extend this limit to 32,767 wide characters, need prepend "\\?\" to the path.
Starting in Windows 10, version 1607, MAX_PATH limitations have been removed from many common Win32 file and directory functions. However, your app must opt-in to the new behavior.
To enable the new long path behavior per application, two conditions must be met. A registry value must be set, and the application manifest must include the longPathAware element. --- dlls/kernel32/tests/file.c | 280 +++++++++++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index db3ffc655ce..9dcba5fa3dd 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -34,6 +34,7 @@ #include "winternl.h" #include "winnls.h" #include "fileapi.h" +#include "winreg.h"
static HANDLE (WINAPI *pFindFirstFileExA)(LPCSTR,FINDEX_INFO_LEVELS,LPVOID,FINDEX_SEARCH_OPS,LPVOID,DWORD); static BOOL (WINAPI *pReplaceFileW)(LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPVOID, LPVOID); @@ -57,6 +58,7 @@ static void (WINAPI *pRtlInitAnsiString)(PANSI_STRING,PCSZ); static void (WINAPI *pRtlFreeUnicodeString)(PUNICODE_STRING); static BOOL (WINAPI *pSetFileCompletionNotificationModes)(HANDLE, UCHAR); static HANDLE (WINAPI *pFindFirstStreamW)(LPCWSTR filename, STREAM_INFO_LEVELS infolevel, void *data, DWORD flags); +static NTSTATUS(WINAPI *pRtlGetVersion)(PRTL_OSVERSIONINFOEXW);
static char filename[MAX_PATH]; static const char sillytext[] = @@ -71,6 +73,17 @@ static const char sillytext[] = "1234 43 4kljf lf &%%%&&&&&& 34 4 34 3############# 33 3 3 3 # 3## 3" "sdlkfjasdlkfj a dslkj adsklf \n \nasdklf askldfa sdlkf \nsadklf asdklf asdf ";
+static const char manifest_long_path_aware[] = +"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +"<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">" +" <assemblyIdentity type="win32" version="1.0.0.0" name="Wine.Test"/>" +" asmv3:application" +" <asmv3:windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings%5C%22%3E" +" ws2:longPathAwaretrue</ws2:longPathAware>" +" </asmv3:windowsSettings>" +" </asmv3:application>" +"</assembly>"; + struct test_list { const char *file; /* file string to test */ const DWORD err; /* Win NT and further error code */ @@ -106,6 +119,7 @@ static void InitFunctionPointers(void) pReOpenFile = (void *) GetProcAddress(hkernel32, "ReOpenFile"); pSetFileCompletionNotificationModes = (void *)GetProcAddress(hkernel32, "SetFileCompletionNotificationModes"); pFindFirstStreamW = (void *)GetProcAddress(hkernel32, "FindFirstStreamW"); + pRtlGetVersion = (void *)GetProcAddress(hntdll, "RtlGetVersion"); }
static void create_file( const char *path ) @@ -904,12 +918,83 @@ static void test_CopyFileA(void) ok(ret, "DeleteFileA: error %ld\n", GetLastError()); }
+/* Windows 10, version 1607, building number 14393, Redstone, August 2, 2016 */ +static BOOL is_win10_1607_or_later(void) +{ + static RTL_OSVERSIONINFOEXW rovi = { 0 }; + + if (rovi.dwOSVersionInfoSize == 0) + { + rovi.dwOSVersionInfoSize = sizeof(rovi); + if (pRtlGetVersion && S_OK == pRtlGetVersion(&rovi)) + trace("windows version: win %ld, build %ld\n", rovi.dwMajorVersion, rovi.dwBuildNumber); + } + + return rovi.dwMajorVersion >= 10 && rovi.dwBuildNumber >= 14393; +} + +static BOOL is_reg_long_path_enabled(void) +{ + static DWORD LongPathEnabled = -1; + + DWORD ret, type, size; + HKEY hkey; + + if (LongPathEnabled == -1) + { + ret = RegOpenKeyExW( HKEY_LOCAL_MACHINE, L"SYSTEM\CurrentControlSet\Control\FileSystem", 0, KEY_READ, &hkey); + if (ret == ERROR_SUCCESS) + { + size = sizeof(LongPathEnabled); + ret = RegQueryValueExW(hkey, L"LongPathsEnabled", NULL, &type, (LPBYTE)&LongPathEnabled, &size); + RegCloseKey(hkey); + } + else + { + LongPathEnabled = 0; + } + trace("in registry LongPathsEnabled: %ld\n", LongPathEnabled); + } + return LongPathEnabled == 1; +} + +static BOOL is_manifest_long_path_enabled(void) +{ + static WCHAR buffer[10] = { 0 }; + static const WCHAR *trueW = L"true"; + BOOL ret; + SIZE_T size; + + if (buffer[0] == 0) + { + memset( buffer, 0xcc, sizeof(buffer) ); + ret = QueryActCtxSettingsW( 0, NULL, L"http://schemas.microsoft.com/SMI/2016/WindowsSettings", L"longPathAware", buffer, lstrlenW(trueW) + 1, &size ); + if (ret && size == lstrlenW(trueW) + 1) + { + trace("in app manifest LongPathAware: %s\n", debugstr_w(buffer)); + } + } + + return lstrcmpiW(buffer, trueW) == 0; +} + +static BOOL is_enabled_long_path(void) +{ + return is_win10_1607_or_later() && is_reg_long_path_enabled() && is_manifest_long_path_enabled(); +} + static void test_CopyFileW(void) { WCHAR temp_path[MAX_PATH]; WCHAR source[MAX_PATH], dest[MAX_PATH]; static const WCHAR prefix[] = {'p','f','x',0}; DWORD ret; + /* long file name, 247 charactors */ + const WCHAR *a = L"a2345678lsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfie.txt"; + const WCHAR *b = L"b2345678lsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfie.txt"; + /* long file path is %TEMP%/%long_name% */ + WCHAR long_path_1[MAX_PATH * 2] = { 0 }; + WCHAR long_path_2[MAX_PATH * 2] = { 0 };
ret = GetTempPathW(MAX_PATH, temp_path); if (ret == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) @@ -942,6 +1027,71 @@ static void test_CopyFileW(void) ok(GetLastError() == ERROR_SUCCESS || broken(GetLastError() == ERROR_INVALID_PARAMETER) /* some win8 machines */, "Unexpected error %lu.\n", GetLastError());
+ /* test long file path, the length of the long_dest is larger than MAX_PATH. */ + if (is_enabled_long_path()) + { + trace("long path is enabled\n"); + wcscpy(long_path_1, temp_path); + wcscat(long_path_1, a); + SetLastError(0xdeadbeef); + ret = CopyFileExW(source, long_path_1, NULL, NULL, NULL, 0); + ok(ret, "CopyFileExW(%s,%s), error %lu.\n", wine_dbgstr_w(source), wine_dbgstr_w(long_path_1), GetLastError()); + + DeleteFileW(dest); + SetLastError(0xdeadbeef); + ret = CopyFileExW(long_path_1, dest, NULL, NULL, NULL, 0); + ok(ret, "CopyFileExW(%s,%s), error %lu.\n", wine_dbgstr_w(long_path_1), wine_dbgstr_w(dest), GetLastError()); + + wcscpy(long_path_2, temp_path); + wcscat(long_path_2, b); + SetLastError(0xdeadbeef); + ret = CopyFileExW(long_path_1, long_path_2, NULL, NULL, NULL, 0); + ok(ret, "CopyFileExW(%s,%s), error %lu.\n", wine_dbgstr_w(long_path_1), wine_dbgstr_w(long_path_2), GetLastError()); + + ret = DeleteFileW(long_path_1); + ok(ret, "Unexpected DeleteFileW successed\n"); + ret = DeleteFileW(long_path_2); + ok(ret, "Unexpected DeleteFileW successed\n"); + } + else + { + wcscpy(long_path_1, temp_path); + wcscat(long_path_1, a); + SetLastError(0xdeadbeef); + ret = CopyFileExW(source, long_path_1, NULL, NULL, NULL, 0); + todo_wine ok(!ret && GetLastError() == ERROR_PATH_NOT_FOUND, "Expected CopyFileExW failed with ERROR_PATH_NOT_FOUND, but got %ld, copy %s -> %s\n", GetLastError(), wine_dbgstr_w(source), wine_dbgstr_w(long_path_1)); + + wcscpy(long_path_2, temp_path); + wcscat(long_path_2, b); + SetLastError(0xdeadbeef); + ret = CopyFileExW(long_path_1, long_path_2, NULL, NULL, NULL, 0); + todo_wine ok(!ret && GetLastError() == ERROR_PATH_NOT_FOUND, "Expected CopyFileExW failed with ERROR_PATH_NOT_FOUND, but got %ld, copy %s -> %s\n", GetLastError(), wine_dbgstr_w(long_path_1), wine_dbgstr_w(long_path_2)); + + ret = DeleteFileW(long_path_1); + todo_wine ok(!ret, "Unexpected DeleteFileW successed\n"); + ret = DeleteFileW(long_path_2); + todo_wine ok(!ret, "Unexpected DeleteFileW successed\n"); + } + + /* test long file name prepend "\?" */ + wcscpy(long_path_1, L"\\?\"); + wcscat(long_path_1, temp_path); + wcscat(long_path_1, a); + SetLastError(0xdeadbeef); + ret = CopyFileExW(source, long_path_1, NULL, NULL, NULL, 0); + ok(ret, "CopyFileExW failed, got %ld, copy %s -> %s failed\n", GetLastError(), wine_dbgstr_w(source), wine_dbgstr_w(long_path_1)); + wcscpy(long_path_2, L"\\?\"); + wcscat(long_path_2, temp_path); + wcscat(long_path_2, b); + SetLastError(0xdeadbeef); + ret = CopyFileExW(long_path_1, long_path_2, NULL, NULL, NULL, 0); + ok(ret, "CopyFileExW failed, got %ld, copy %s -> %s failed\n", GetLastError(), wine_dbgstr_w(long_path_1), wine_dbgstr_w(long_path_2)); + + ret = DeleteFileW(long_path_1); + ok(ret, "DeleteFilew: error %lu\n", GetLastError()); + ret = DeleteFileW(long_path_2); + ok(ret, "DeleteFileW: error %lu\n", GetLastError()); + ret = DeleteFileW(source); ok(ret, "DeleteFileW: error %ld\n", GetLastError()); ret = DeleteFileW(dest); @@ -1194,6 +1344,12 @@ static void test_CopyFileEx(void) HANDLE hfile; DWORD ret; BOOL retok; + /* long file name, 247 charactors */ + const char *a = "a2345678lsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfie.txt"; + const char *b = "b2345678lsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfie.txt"; + /* long file path is %TEMP%/%long_name% */ + char long_path_1[MAX_PATH * 2] = { 0 }; + char long_path_2[MAX_PATH * 2] = { 0 };
ret = GetTempPathA(MAX_PATH, temp_path); ok(ret != 0, "GetTempPathA error %ld\n", GetLastError()); @@ -1235,6 +1391,69 @@ static void test_CopyFileEx(void) ok(!retok, "CopyFileExA unexpectedly succeeded\n"); ok(GetLastError() == ERROR_PATH_NOT_FOUND, "expected ERROR_PATH_NOT_FOUND, got %ld\n", GetLastError());
+ /* test long file path, the length of the long_dest is larger than MAX_PATH. */ + if (is_enabled_long_path()) + { + trace("long path is enabled\n"); + strcpy(long_path_1, temp_path); + strcat(long_path_1, a); + SetLastError(0xdeadbeef); + retok = CopyFileExA(source, long_path_1, NULL, NULL, NULL, 0); + ok(retok, "Expected CopyFileExA succeeded, but got %ld, copy %s -> %s\n", GetLastError(), source, long_path_1); + + DeleteFileA(dest); + SetLastError(0xdeadbeef); + retok = CopyFileExA(long_path_1, dest, NULL, NULL, NULL, 0); + ok(retok, "Expected CopyFileExA successed, but got %ld, copy %s -> %s\n", GetLastError(), long_path_1, dest); + DeleteFileA(dest); + + strcpy(long_path_2, temp_path); + strcat(long_path_2, b); + SetLastError(0xdeadbeef); + retok = CopyFileExA(long_path_1, long_path_2, NULL, NULL, NULL, 0); + ok(retok, "Expected CopyFileExA succeeded, but got %ld, copy %s -> %s\n", GetLastError(), long_path_1, long_path_2); + retok = DeleteFileA(long_path_1); + todo_wine ok(retok, "DeleteFileA failed: %ld, %s\n", GetLastError(), long_path_1); + retok = DeleteFileA(long_path_2); + todo_wine ok(retok, "DeleteFileA failed: %ld, %s\n", GetLastError(), long_path_2); + } + else + { + strcpy(long_path_1, temp_path); + strcat(long_path_1, a); + SetLastError(0xdeadbeef); + retok = CopyFileExA(source, long_path_1, NULL, NULL, NULL, 0); + todo_wine ok(!retok && GetLastError() == ERROR_PATH_NOT_FOUND, "Expected CopyFileExA failed with ERROR_PATH_NOT_FOUND, but got %ld, copy %s -> %s\n", GetLastError(), source, long_path_1); + strcpy(long_path_2, temp_path); + strcat(long_path_2, b); + SetLastError(0xdeadbeef); + retok = CopyFileExA(long_path_1, long_path_2, NULL, NULL, NULL, 0); + todo_wine ok(!retok && GetLastError() == ERROR_PATH_NOT_FOUND, "Expected CopyFileExA failed with ERROR_PATH_NOT_FOUND, but got %ld, copy %s -> %s\n", GetLastError(), long_path_1, long_path_2); + retok = DeleteFileA(long_path_1); + todo_wine ok(!retok, "Unexpected DeleteFileA successed\n"); + retok = DeleteFileA(long_path_2); + todo_wine ok(!retok, "Unexpected DeleteFileA successed\n"); + } + + /* test long file name prepend "\?" */ + strcpy(long_path_1, "\\?\"); + strcat(long_path_1, temp_path); + strcat(long_path_1, a); + SetLastError(0xdeadbeef); + retok = CopyFileExA(source, long_path_1, NULL, NULL, NULL, 0); + ok(retok, "CopyFileExA failed, got %ld, copy %s -> %s failed\n", GetLastError(), source, long_path_1); + strcpy(long_path_2, "\\?\"); + strcat(long_path_2, temp_path); + strcat(long_path_2, b); + SetLastError(0xdeadbeef); + retok = CopyFileExA(long_path_1, long_path_2, NULL, NULL, NULL, 0); + ok(retok, "CopyFileExA failed, got %ld, copy %s -> %s failed\n", GetLastError(), long_path_1, long_path_2); + + retok = DeleteFileA(long_path_1); + todo_wine ok(retok, "DeleteFileA failed: %lu, %s\n", GetLastError(), long_path_1); + retok = DeleteFileA(long_path_2); + todo_wine ok(retok, "DeleteFileA failed: %lu, %s\n", GetLastError(), long_path_2); + ret = DeleteFileA(source); ok(ret, "DeleteFileA failed with error %ld\n", GetLastError()); ret = DeleteFileA(dest); @@ -6317,10 +6536,68 @@ static void test_eof(void) ok(ret, "failed to delete %s, error %lu\n", debugstr_a(filename), GetLastError()); }
+static BOOL create_manifest_file(const char *filename) +{ + DWORD size; + HANDLE file; + + file = CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + ok(file != INVALID_HANDLE_VALUE, "CreateFile failed: %lu\n", GetLastError()); + if(file == INVALID_HANDLE_VALUE) + return FALSE; + WriteFile(file, manifest_long_path_aware, strlen(manifest_long_path_aware), &size, NULL); + CloseHandle(file); + + return TRUE; +} + +static void run_child_process(void) +{ + char cmdline[MAX_PATH]; + char path[MAX_PATH]; + char **argv; + PROCESS_INFORMATION pi; + STARTUPINFOA si = { 0 }; + HANDLE file; + FILETIME now; + BOOL ret; + + GetModuleFileNameA(NULL, path, MAX_PATH); + strcat(path, ".manifest"); + if(!create_manifest_file(path)) { + skip("Could not create manifest file\n"); + return; + } + + si.cb = sizeof(si); + winetest_get_mainargs( &argv ); + /* Vista+ seems to cache presence of .manifest files. Change last modified + date to defeat the cache */ + file = CreateFileA(argv[0], FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + if (file != INVALID_HANDLE_VALUE) { + GetSystemTimeAsFileTime(&now); + SetFileTime(file, NULL, NULL, &now); + CloseHandle(file); + } + sprintf(cmdline, ""%s" %s manifest", argv[0], argv[1]); + ret = CreateProcessA(argv[0], cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + ok(ret, "Could not create process: %lu\n", GetLastError()); + wait_child_process( pi.hProcess ); + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + DeleteFileA(path); +} + START_TEST(file) { char temp_path[MAX_PATH]; DWORD ret; + int argc; + char **argv; + + argc = winetest_get_mainargs(&argv);
InitFunctionPointers();
@@ -6395,4 +6672,7 @@ START_TEST(file) test_hard_link(); test_move_file(); test_eof(); + + if (argc <= 2) + run_child_process(); }
From: yaoyongjie yaoyongjie@uniontech.com
--- dlls/kernel32/tests/file.c | 84 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index 9dcba5fa3dd..87cfb7f7a8a 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -2084,6 +2084,10 @@ static void test_DeleteFileA( void ) char temp_path[MAX_PATH], temp_file[MAX_PATH]; HANDLE hfile, mapping; char **argv; + /* long file name, 247 charactors */ + const char *a = "a2345678lsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfie.txt"; + /* long file path is %TEMP%/%long_name% */ + char long_path[MAX_PATH * 2] = { 0 };
ret = DeleteFileA(NULL); ok(!ret && (GetLastError() == ERROR_INVALID_PARAMETER || @@ -2152,6 +2156,39 @@ static void test_DeleteFileA( void ) ok(ret, "got error %lu\n", GetLastError());
CloseHandle(hfile); + + SetLastError(0xdeadbeef); + GetTempPathA(MAX_PATH, temp_path); + strcpy(long_path, "\\?\"); + strcat(long_path, temp_path); + strcat(long_path, a); + ret = CopyFileExA(argv[0], long_path, NULL, NULL, NULL, 0); + ok(ret, "got error %lu\n", GetLastError()); + + if (is_enabled_long_path()) + { + strcpy(long_path, temp_path); + strcat(long_path, a); + ret = DeleteFileA(long_path); + todo_wine ok(ret, "DeleteFileA failed with error %ld\n", GetLastError()); + } + else + { + strcpy(long_path, "\\?\"); + strcat(long_path, temp_path); + strcat(long_path, a); + SetLastError(0xdeadbeef); + ret = DeleteFileA(long_path); + todo_wine ok(ret, "DeleteFileA failed with error %ld\n", GetLastError()); + + ret = CopyFileExA(argv[0], long_path, NULL, NULL, NULL, 0); + ok(ret, "got error %lu\n", GetLastError()); + + strcpy(long_path, temp_path); + strcat(long_path, a); + ret = DeleteFileA(long_path); + todo_wine ok(!ret, "Unexpected DeleteFileA successed, %s\n", long_path); + } }
static void test_DeleteFileW( void ) @@ -2163,6 +2200,12 @@ static void test_DeleteFileW( void ) static const WCHAR subdirW[] = {'\','s','u','b',0}; static const WCHAR emptyW[]={'\0'};
+ /* long file name, 247 charactors */ + const WCHAR *a = L"a2345678lsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfielfelfjellsfalfielsfleflsfie.txt"; + /* long file path is %TEMP%/%long_name% */ + WCHAR long_path[MAX_PATH * 2] = { 0 }; + HANDLE hfile; + ret = DeleteFileW(NULL); if (ret == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) { @@ -2204,6 +2247,47 @@ static void test_DeleteFileW( void ) ok(ret == TRUE, "expected to remove directory deletefile\sub\n"); ret = RemoveDirectoryW(pathW); ok(ret == TRUE, "expected to remove directory deletefile\n"); + + SetLastError(0xdeadbeef); + GetTempPathW(MAX_PATH, pathW); + wcscpy(pathsubW, pathW); + wcscat(pathsubW, L"\test.txt"); + hfile = CreateFileW(pathsubW, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0); + ok(hfile != INVALID_HANDLE_VALUE , "got error %lu\n", GetLastError()); + CloseHandle(hfile); + + wcscpy(long_path, L"\\?\"); + wcscat(long_path, pathW); + wcscat(long_path, a); + ret = CopyFileExW(pathsubW, long_path, NULL, NULL, NULL, 0); + ok(ret, "got error %lu\n", GetLastError()); + + if (is_enabled_long_path()) + { + wcscpy(long_path, pathW); + wcscat(long_path, a); + ret = DeleteFileW(long_path); + todo_wine ok(ret, "DeleteFileW: error %ld\n", GetLastError()); + } + else + { + wcscpy(long_path, L"\\?\"); + wcscat(long_path, pathW); + wcscat(long_path, a); + SetLastError(0xdeadbeef); + ret = DeleteFileW(long_path); + todo_wine ok(ret, "DeleteFileW: error %ld\n", GetLastError()); + + ret = CopyFileExW(pathsubW, long_path, NULL, NULL, NULL, 0); + ok(ret, "got error %lu\n", GetLastError()); + + wcscpy(long_path, pathW); + wcscat(long_path, a); + ret = DeleteFileW(long_path); + todo_wine ok(!ret, "Unexpected DeleteFileW successed, %s\n", wine_dbgstr_w(long_path)); + } + + DeleteFileW(pathsubW); }
#define IsDotDir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
From: yaoyongjie yaoyongjie@uniontech.com
--- dlls/ntdll/actctx.c | 60 +++++++++++++++++++++++++++++++++++++++++ dlls/ntdll/loader.c | 2 +- dlls/ntdll/ntdll_misc.h | 2 ++ 3 files changed, 63 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/actctx.c b/dlls/ntdll/actctx.c index 9828d90d11d..656875c9b55 100644 --- a/dlls/ntdll/actctx.c +++ b/dlls/ntdll/actctx.c @@ -5223,11 +5223,61 @@ static const WCHAR *find_app_settings( ACTIVATION_CONTEXT *actctx, const WCHAR * return NULL; }
+/* Windows 10, version 1607, building number 14393, Redstone, August 2, 2016 */ +static BOOL is_win10_1607_or_later(void) +{ + static RTL_OSVERSIONINFOEXW rovi = { 0 }; + + if (rovi.dwOSVersionInfoSize == 0) + { + rovi.dwOSVersionInfoSize = sizeof(rovi); + if (RtlGetVersion(&rovi) == ERROR_SUCCESS) + { + TRACE("windows version: win %ld, build %ld\n", rovi.dwMajorVersion, rovi.dwBuildNumber); + } + else + { + ERR("RtlGetVersion failed\n"); + rovi.dwMajorVersion = 0; + rovi.dwBuildNumber = 0; + } + } + + return rovi.dwMajorVersion >= 10 && rovi.dwBuildNumber >= 14393; +} + +static BOOL is_reg_long_path_enabled(void) +{ + static DWORD LongPathEnabled = -1; + HANDLE hkey; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING file_system_str = + RTL_CONSTANT_STRING( L"\Registry\Machine\System\CurrentControlSet\Control\FileSystem" ); + + if (LongPathEnabled == -1) + { + InitializeObjectAttributes( &attr, &file_system_str, OBJ_CASE_INSENSITIVE, 0, NULL ); + if (!NtOpenKey( &hkey, KEY_QUERY_VALUE, &attr )) + { + query_dword_option(hkey, L"LongPathsEnabled", (LONG*)&LongPathEnabled); + NtClose(hkey); + } + else + { + LongPathEnabled = 0; + } + TRACE("in registry LongPathsEnabled: %ld\n", LongPathEnabled); + } + return LongPathEnabled == 1; +} + + /* initialize the activation context for the current process */ void actctx_init(void) { ACTCTXW ctx; HANDLE handle; + WCHAR buffer[5];
ctx.cbSize = sizeof(ctx); ctx.lpSource = NULL; @@ -5238,6 +5288,16 @@ void actctx_init(void) if (!RtlCreateActivationContext( &handle, &ctx )) process_actctx = check_actctx(handle);
NtCurrentTeb()->Peb->ActivationContextData = process_actctx; + + if (!RtlQueryActivationContextApplicationSettings( 0, NULL, L"http://schemas.microsoft.com/SMI/2016/WindowsSettings", + L"longPathAware", buffer, ARRAY_SIZE(buffer), NULL )) + { + TRACE( "got longPathAware=%s\n", debugstr_w(buffer) ); + if (!wcsicmp( buffer, L"true" ) && is_win10_1607_or_later() && is_reg_long_path_enabled()) + { + NtCurrentTeb()->Peb->IsLongPathAwareProcess = 1; + } + } }
diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index 6b089c990b5..f89d577fbd8 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -3687,7 +3687,7 @@ NTSTATUS WINAPI LdrQueryProcessModuleInformation(RTL_PROCESS_MODULES *smi, }
-static NTSTATUS query_dword_option( HANDLE hkey, LPCWSTR name, LONG *value ) +NTSTATUS query_dword_option( HANDLE hkey, LPCWSTR name, LONG *value ) { NTSTATUS status; UNICODE_STRING str; diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index f82edef6b65..5bfb5f262b4 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -186,4 +186,6 @@ extern void *__os_arm64x_helper8;
#endif
+extern NTSTATUS query_dword_option( HANDLE hkey, LPCWSTR name, LONG *value ); + #endif
From: yaoyongjie yaoyongjie@uniontech.com
In CopyFileEx, by default, the file name and path are limited to MAX_PATH characters. To extend this limit to 32,767 wide characters, need prepend "\\?\" to the path. --- dlls/kernel32/tests/file.c | 8 ++++---- dlls/kernelbase/file.c | 12 ++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index 87cfb7f7a8a..a98c48ccd7b 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -1059,13 +1059,13 @@ static void test_CopyFileW(void) wcscat(long_path_1, a); SetLastError(0xdeadbeef); ret = CopyFileExW(source, long_path_1, NULL, NULL, NULL, 0); - todo_wine ok(!ret && GetLastError() == ERROR_PATH_NOT_FOUND, "Expected CopyFileExW failed with ERROR_PATH_NOT_FOUND, but got %ld, copy %s -> %s\n", GetLastError(), wine_dbgstr_w(source), wine_dbgstr_w(long_path_1)); + ok(!ret && GetLastError() == ERROR_PATH_NOT_FOUND, "Expected CopyFileExW failed with ERROR_PATH_NOT_FOUND, but got %ld, copy %s -> %s\n", GetLastError(), wine_dbgstr_w(source), wine_dbgstr_w(long_path_1));
wcscpy(long_path_2, temp_path); wcscat(long_path_2, b); SetLastError(0xdeadbeef); ret = CopyFileExW(long_path_1, long_path_2, NULL, NULL, NULL, 0); - todo_wine ok(!ret && GetLastError() == ERROR_PATH_NOT_FOUND, "Expected CopyFileExW failed with ERROR_PATH_NOT_FOUND, but got %ld, copy %s -> %s\n", GetLastError(), wine_dbgstr_w(long_path_1), wine_dbgstr_w(long_path_2)); + ok(!ret && GetLastError() == ERROR_PATH_NOT_FOUND, "Expected CopyFileExW failed with ERROR_PATH_NOT_FOUND, but got %ld, copy %s -> %s\n", GetLastError(), wine_dbgstr_w(long_path_1), wine_dbgstr_w(long_path_2));
ret = DeleteFileW(long_path_1); todo_wine ok(!ret, "Unexpected DeleteFileW successed\n"); @@ -1423,12 +1423,12 @@ static void test_CopyFileEx(void) strcat(long_path_1, a); SetLastError(0xdeadbeef); retok = CopyFileExA(source, long_path_1, NULL, NULL, NULL, 0); - todo_wine ok(!retok && GetLastError() == ERROR_PATH_NOT_FOUND, "Expected CopyFileExA failed with ERROR_PATH_NOT_FOUND, but got %ld, copy %s -> %s\n", GetLastError(), source, long_path_1); + ok(!retok && GetLastError() == ERROR_PATH_NOT_FOUND, "Expected CopyFileExA failed with ERROR_PATH_NOT_FOUND, but got %ld, copy %s -> %s\n", GetLastError(), source, long_path_1); strcpy(long_path_2, temp_path); strcat(long_path_2, b); SetLastError(0xdeadbeef); retok = CopyFileExA(long_path_1, long_path_2, NULL, NULL, NULL, 0); - todo_wine ok(!retok && GetLastError() == ERROR_PATH_NOT_FOUND, "Expected CopyFileExA failed with ERROR_PATH_NOT_FOUND, but got %ld, copy %s -> %s\n", GetLastError(), long_path_1, long_path_2); + ok(!retok && GetLastError() == ERROR_PATH_NOT_FOUND, "Expected CopyFileExA failed with ERROR_PATH_NOT_FOUND, but got %ld, copy %s -> %s\n", GetLastError(), long_path_1, long_path_2); retok = DeleteFileA(long_path_1); todo_wine ok(!retok, "Unexpected DeleteFileA successed\n"); retok = DeleteFileA(long_path_2); diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 7d8b6844456..0f70e28063f 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -527,6 +527,18 @@ static BOOL copy_file( const WCHAR *source, const WCHAR *dest, COPYFILE2_EXTENDE SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } + + if (wcslen(source) >= MAX_PATH && wcsncmp(source, L"\\?\", 4) && !RtlGetCurrentPeb()->IsLongPathAwareProcess) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + if (wcslen(dest) >= MAX_PATH && wcsncmp(dest, L"\\?\", 4) && !RtlGetCurrentPeb()->IsLongPathAwareProcess) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + if (!(buffer = HeapAlloc( GetProcessHeap(), 0, buffer_size ))) { SetLastError( ERROR_NOT_ENOUGH_MEMORY );
From: yaoyongjie yaoyongjie@uniontech.com
If alloc is FALSE, file_name_AtoW will uses the TEB static buffer, but the buffer size is MAX_PATH+1 . If DeleteFileA is to support long paths prefixed with "\\?\", alloc must be set to TRUE. --- dlls/kernel32/tests/file.c | 16 ++++++++-------- dlls/kernelbase/file.c | 7 +++++-- 2 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index a98c48ccd7b..d841a5cbd17 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -1413,9 +1413,9 @@ static void test_CopyFileEx(void) retok = CopyFileExA(long_path_1, long_path_2, NULL, NULL, NULL, 0); ok(retok, "Expected CopyFileExA succeeded, but got %ld, copy %s -> %s\n", GetLastError(), long_path_1, long_path_2); retok = DeleteFileA(long_path_1); - todo_wine ok(retok, "DeleteFileA failed: %ld, %s\n", GetLastError(), long_path_1); + ok(retok, "DeleteFileA failed: %ld, %s\n", GetLastError(), long_path_1); retok = DeleteFileA(long_path_2); - todo_wine ok(retok, "DeleteFileA failed: %ld, %s\n", GetLastError(), long_path_2); + ok(retok, "DeleteFileA failed: %ld, %s\n", GetLastError(), long_path_2); } else { @@ -1430,9 +1430,9 @@ static void test_CopyFileEx(void) retok = CopyFileExA(long_path_1, long_path_2, NULL, NULL, NULL, 0); ok(!retok && GetLastError() == ERROR_PATH_NOT_FOUND, "Expected CopyFileExA failed with ERROR_PATH_NOT_FOUND, but got %ld, copy %s -> %s\n", GetLastError(), long_path_1, long_path_2); retok = DeleteFileA(long_path_1); - todo_wine ok(!retok, "Unexpected DeleteFileA successed\n"); + ok(!retok, "Unexpected DeleteFileA successed\n"); retok = DeleteFileA(long_path_2); - todo_wine ok(!retok, "Unexpected DeleteFileA successed\n"); + ok(!retok, "Unexpected DeleteFileA successed\n"); }
/* test long file name prepend "\?" */ @@ -1450,9 +1450,9 @@ static void test_CopyFileEx(void) ok(retok, "CopyFileExA failed, got %ld, copy %s -> %s failed\n", GetLastError(), long_path_1, long_path_2);
retok = DeleteFileA(long_path_1); - todo_wine ok(retok, "DeleteFileA failed: %lu, %s\n", GetLastError(), long_path_1); + ok(retok, "DeleteFileA failed: %lu, %s\n", GetLastError(), long_path_1); retok = DeleteFileA(long_path_2); - todo_wine ok(retok, "DeleteFileA failed: %lu, %s\n", GetLastError(), long_path_2); + ok(retok, "DeleteFileA failed: %lu, %s\n", GetLastError(), long_path_2);
ret = DeleteFileA(source); ok(ret, "DeleteFileA failed with error %ld\n", GetLastError()); @@ -2170,7 +2170,7 @@ static void test_DeleteFileA( void ) strcpy(long_path, temp_path); strcat(long_path, a); ret = DeleteFileA(long_path); - todo_wine ok(ret, "DeleteFileA failed with error %ld\n", GetLastError()); + ok(ret, "DeleteFileA failed with error %ld\n", GetLastError()); } else { @@ -2179,7 +2179,7 @@ static void test_DeleteFileA( void ) strcat(long_path, a); SetLastError(0xdeadbeef); ret = DeleteFileA(long_path); - todo_wine ok(ret, "DeleteFileA failed with error %ld\n", GetLastError()); + ok(ret, "DeleteFileA failed with error %ld\n", GetLastError());
ret = CopyFileExA(argv[0], long_path, NULL, NULL, NULL, 0); ok(ret, "got error %lu\n", GetLastError()); diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 0f70e28063f..152b9fb8771 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -1005,9 +1005,12 @@ BOOLEAN WINAPI /* DECLSPEC_HOTPATCH */ CreateSymbolicLinkW( LPCWSTR link, LPCWST BOOL WINAPI DECLSPEC_HOTPATCH DeleteFileA( LPCSTR path ) { WCHAR *pathW; + BOOL res;
- if (!(pathW = file_name_AtoW( path, FALSE ))) return FALSE; - return DeleteFileW( pathW ); + if (!(pathW = file_name_AtoW( path, TRUE ))) return FALSE; + res = DeleteFileW( pathW ); + HeapFree( GetProcessHeap(), 0, pathW ); + return res; }
From: yaoyongjie yaoyongjie@uniontech.com
In DeleteFile functions, by default, the file name and path are limited to MAX_PATH characters. To extend this limit to 32,767 wide characters, need prepend "\\?\" to the path. --- dlls/kernel32/tests/file.c | 12 ++++++------ dlls/kernelbase/file.c | 6 ++++++ 2 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index d841a5cbd17..d955ced842e 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -1068,9 +1068,9 @@ static void test_CopyFileW(void) ok(!ret && GetLastError() == ERROR_PATH_NOT_FOUND, "Expected CopyFileExW failed with ERROR_PATH_NOT_FOUND, but got %ld, copy %s -> %s\n", GetLastError(), wine_dbgstr_w(long_path_1), wine_dbgstr_w(long_path_2));
ret = DeleteFileW(long_path_1); - todo_wine ok(!ret, "Unexpected DeleteFileW successed\n"); + ok(!ret, "Unexpected DeleteFileW successed\n"); ret = DeleteFileW(long_path_2); - todo_wine ok(!ret, "Unexpected DeleteFileW successed\n"); + ok(!ret, "Unexpected DeleteFileW successed\n"); }
/* test long file name prepend "\?" */ @@ -2187,7 +2187,7 @@ static void test_DeleteFileA( void ) strcpy(long_path, temp_path); strcat(long_path, a); ret = DeleteFileA(long_path); - todo_wine ok(!ret, "Unexpected DeleteFileA successed, %s\n", long_path); + ok(!ret, "Unexpected DeleteFileA successed, %s\n", long_path); } }
@@ -2267,7 +2267,7 @@ static void test_DeleteFileW( void ) wcscpy(long_path, pathW); wcscat(long_path, a); ret = DeleteFileW(long_path); - todo_wine ok(ret, "DeleteFileW: error %ld\n", GetLastError()); + ok(ret, "DeleteFileW: error %ld\n", GetLastError()); } else { @@ -2276,7 +2276,7 @@ static void test_DeleteFileW( void ) wcscat(long_path, a); SetLastError(0xdeadbeef); ret = DeleteFileW(long_path); - todo_wine ok(ret, "DeleteFileW: error %ld\n", GetLastError()); + ok(ret, "DeleteFileW: error %ld\n", GetLastError());
ret = CopyFileExW(pathsubW, long_path, NULL, NULL, NULL, 0); ok(ret, "got error %lu\n", GetLastError()); @@ -2284,7 +2284,7 @@ static void test_DeleteFileW( void ) wcscpy(long_path, pathW); wcscat(long_path, a); ret = DeleteFileW(long_path); - todo_wine ok(!ret, "Unexpected DeleteFileW successed, %s\n", wine_dbgstr_w(long_path)); + ok(!ret, "Unexpected DeleteFileW successed, %s\n", wine_dbgstr_w(long_path)); }
DeleteFileW(pathsubW); diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 152b9fb8771..9c04041bf59 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -1033,6 +1033,12 @@ BOOL WINAPI DECLSPEC_HOTPATCH DeleteFileW( LPCWSTR path ) return FALSE; }
+ if (wcslen(path) >= MAX_PATH && wcsncmp(path, L"\\?\", 4) && !RtlGetCurrentPeb()->IsLongPathAwareProcess) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + InitializeObjectAttributes( &attr, &nameW, OBJ_CASE_INSENSITIVE, 0, NULL ); status = NtCreateFile(&hFile, SYNCHRONIZE | DELETE, &attr, &io, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,